✨ CAMEL-19449: spring-boot platform-http example (#83)
diff --git a/README.adoc b/README.adoc
index 2fa3b3b..b6936a2 100644
--- a/README.adoc
+++ b/README.adoc
@@ -134,6 +134,8 @@
| link:paho-mqtt5-shared-subscriptions/README.adoc[Paho Mqtt5 Shared Subscriptions] (paho-mqtt5-shared-subscriptions) | Messaging | An example showing how to set up multiple mqtt5 consumers that use shared subscription feature of MQTT5
+| link:platform-http/README.adoc[Platform HTTP] (platform-http) | Rest | An example showing how to use the platform-http component and the servlet-starter
+
| link:rabbitmq/readme.adoc[Rabbitmq] (rabbitmq) | Messaging | An example showing how to work with Camel and RabbitMQ
| link:strimzi/README.adoc[Strimzi] (strimzi) | Messaging | Camel example which a route is defined in XML for Strimzi integration on Openshift/Kubernetes
diff --git a/platform-http/README.adoc b/platform-http/README.adoc
new file mode 100644
index 0000000..6f6db75
--- /dev/null
+++ b/platform-http/README.adoc
@@ -0,0 +1,122 @@
+== Spring Boot Example with Camel REST DSL and Platform HTTP
+
+=== Introduction
+
+This example illustrates how to use https://projects.spring.io/spring-boot/[Spring Boot] with http://camel.apache.org[Camel]. It provides a simple REST service that is created with http://camel.apache.org/rest-dsl.html[Camel REST DSL] and https://camel.apache.org/components/3.18.x/platform-http-component.html[platform-http].
+
+The project uses the `camel-spring-boot-starter` dependency, a Spring Boot starter dependency for Camel that simplifies the Maven configuration.
+
+The project also uses `camel-servlet-starter` component as the implementation for platform-http-engine.
+
+=== Build
+
+You can build this example using:
+
+ $ mvn package
+
+=== Run
+
+You can run this example using:
+
+ $ mvn spring-boot:run
+
+You should see the following output when the application is launched:
+
+[source,text]
+----
+
+ . ____ _ __ _ _
+ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/ ___)| |_)| | | | | || (_| | ) ) ) )
+ ' |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ ...
+2022-09-05 12:00:31.101 INFO 16692 --- [ main] o.a.c.c.s.CamelHttpTransportServlet : Initialized CamelHttpTransportServlet[name=CamelServlet, contextPath=]
+2022-09-05 12:00:31.113 INFO 16692 --- [ main] io.undertow : starting server: Undertow - 2.2.19.Final
+2022-09-05 12:00:31.128 INFO 16692 --- [ main] org.xnio : XNIO version 3.8.7.Final
+2022-09-05 12:00:31.144 INFO 16692 --- [ main] org.xnio.nio : XNIO NIO Implementation Version 3.8.7.Final
+2022-09-05 12:00:31.354 INFO 16692 --- [ main] org.jboss.threads : JBoss Threads version 3.1.0.Final
+2022-09-05 12:00:31.453 INFO 16692 --- [ main] o.s.b.w.e.undertow.UndertowWebServer : Undertow started on port(s) 8080 (http)
+2022-09-05 12:00:31.955 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 3.19.0-SNAPSHOT (MyCamel) is starting
+2022-09-05 12:00:31.989 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Routes startup (started:7)
+2022-09-05 12:00:31.989 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started route1 (direct://test)
+2022-09-05 12:00:31.989 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started route2 (rest://get:/todos)
+2022-09-05 12:00:31.989 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started route3 (rest://get:/todos:/%7Bid%7D)
+2022-09-05 12:00:31.990 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started route4 (rest://patch:/todos:/%7Bid%7D)
+2022-09-05 12:00:31.990 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started route5 (rest://post:/todos)
+2022-09-05 12:00:31.990 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started route6 (rest://delete:/todos)
+2022-09-05 12:00:31.991 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started route7 (rest://delete:/todos:/%7Bid%7D)
+2022-09-05 12:00:31.992 INFO 16692 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 3.19.0-SNAPSHOT (MyCamel) started in 552ms (build:95ms init:421ms start:36ms)
+2022-09-05 12:00:32.005 INFO 16692 --- [ main] o.a.c.example.springboot.Application : Started Application in 13.737 seconds (JVM running for 14.657)
+----
+
+After the Spring Boot application is started, you can execute the following HTTP requests:
+
+Create a TODO
+
+[source,text]
+----
+$ curl -d '{"title":"Todo title", "completed":"false", "order": 1, "url":""}' -H "Content-Type: application/json" -X POST http://localhost:8080/api/todos
+----
+
+The command will produce the following output:
+
+[source,json]
+----
+{"id":1,"title":"Todo title","completed":false,"order":1,"url":""}
+----
+
+Retrieve all TODOs
+
+[source,text]
+----
+$ curl http://localhost:8080/api/todos
+----
+
+The command will produce the following output:
+
+[source,json]
+----
+[{"id":1,"title":"Todo title","completed":false,"order":1,"url":""}]
+----
+
+Update one TODO
+
+[source,text]
+----
+$ curl -d '{"title":"Todo title", "completed":"true", "order": 1, "url":""}' -H "Content-Type: application/json" -X PATCH http://localhost:8080/api/todos/1
+----
+
+The command will produce the following output:
+
+[source,json]
+----
+{"id":1,"title":"Todo title","completed":true,"order":1,"url":""}
+----
+
+Delete completed TODOs
+
+[source,text]
+----
+$ curl -X "DELETE" http://localhost:8080/api/todos
+----
+
+The command will produce the following output:
+
+[source,json]
+----
+1
+----
+
+The Spring Boot application can be stopped pressing `[CTRL] + [C]` in the shell.
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/support.html[let us know].
+
+We also love contributors, so
+https://camel.apache.org/contributing.html[get involved] :-)
+
+The Camel riders!
diff --git a/platform-http/pom.xml b/platform-http/pom.xml
new file mode 100644
index 0000000..f747bba
--- /dev/null
+++ b/platform-http/pom.xml
@@ -0,0 +1,122 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel.springboot.example</groupId>
+ <artifactId>examples</artifactId>
+ <version>3.19.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-example-spring-boot-platform-http</artifactId>
+ <name>Camel SB Examples :: REST DSL and Platform HTTP</name>
+ <description>An example showing Camel REST DSL with platform HTTP</description>
+
+ <properties>
+ <category>Rest</category>
+ </properties>
+
+ <!-- Spring-Boot and Camel BOM -->
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>${spring-boot-version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-spring-boot-bom</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <!-- Spring Boot -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-tomcat</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-undertow</artifactId>
+ </dependency>
+
+ <!-- Camel -->
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-spring-boot-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-platform-http-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-servlet-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-jackson-starter</artifactId>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-junit5</artifactId>
+ <version>${camel-version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${spring-boot-version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/platform-http/src/main/java/org/apache/camel/example/springboot/Application.java b/platform-http/src/main/java/org/apache/camel/example/springboot/Application.java
new file mode 100644
index 0000000..4c0bd71
--- /dev/null
+++ b/platform-http/src/main/java/org/apache/camel/example/springboot/Application.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+// CHECKSTYLE:OFF
+@SpringBootApplication
+public class Application {
+
+ /**
+ * Main method to start the application.
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
+// CHECKSTYLE:ON
diff --git a/platform-http/src/main/java/org/apache/camel/example/springboot/CamelRouter.java b/platform-http/src/main/java/org/apache/camel/example/springboot/CamelRouter.java
new file mode 100644
index 0000000..c3b264f
--- /dev/null
+++ b/platform-http/src/main/java/org/apache/camel/example/springboot/CamelRouter.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot;
+
+import static org.apache.camel.model.rest.RestParamType.body;
+import static org.apache.camel.model.rest.RestParamType.path;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.RestBindingMode;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * A simple Camel REST DSL route.
+ */
+@Component
+public class CamelRouter extends RouteBuilder {
+
+ @Override
+ public void configure() throws Exception {
+ restConfiguration()
+ .bindingMode(RestBindingMode.json);
+
+ // @formatter:off
+ rest("/todos").description("Todo REST service")
+ .consumes("application/json")
+ .produces("application/json")
+
+ .get().description("Find all todos").outType(Todo[].class)
+ .responseMessage().code(200).message("All todos successfully returned").endResponseMessage()
+ .to("bean:todoService?method=listAll")
+
+ .get("/{id}").description("Find todo by ID")
+ .outType(Todo.class)
+ .param().name("id").type(path).description("The ID of the todo").dataType("long").endParam()
+ .responseMessage().code(200).message("Todo successfully returned").endResponseMessage()
+ .to("bean:todoService?method=findById(${header.id})")
+
+ .patch("/{id}").description("Update a todo").type(Todo.class)
+ .param().name("id").type(path).description("The ID of the todo to update").dataType("long").endParam()
+ .param().name("body").type(body).description("The todo to update").endParam()
+ .responseMessage().code(204).message("Todo successfully updated").endResponseMessage()
+ .to("bean:todoService?method=update(${body}, ${header.id})")
+
+ .post().description("Create a todo").type(Todo.class)
+ .param().name("body").type(body).endParam()
+ .responseMessage().code(201).message("Todo successfully created").endResponseMessage()
+ .to("bean:todoService?method=create")
+
+ .delete().description("Delete completed todos")
+ .responseMessage().code(200).message("Todos deleted").endResponseMessage()
+ .to("bean:todoService?method=deleteCompleted")
+
+ .delete("/{id}").description("Delete by id")
+ .param().name("id").type(path).description("The ID of the todo to delete").dataType("long").endParam()
+ .responseMessage().code(200).message("Todo deleted").endResponseMessage()
+ .to("bean:todoService?method=deleteOne(${header.id})");
+ // @formatter:on
+ }
+}
diff --git a/platform-http/src/main/java/org/apache/camel/example/springboot/Todo.java b/platform-http/src/main/java/org/apache/camel/example/springboot/Todo.java
new file mode 100644
index 0000000..e75f28b
--- /dev/null
+++ b/platform-http/src/main/java/org/apache/camel/example/springboot/Todo.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot;
+
+public class Todo {
+ private long id;
+ private String title;
+ private boolean completed;
+ private int order;
+ private String url;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ public void setCompleted(boolean completed) {
+ this.completed = completed;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public String toString() {
+ return "Todo{" +
+ "id=" + id +
+ ", title='" + title + '\'' +
+ ", completed=" + completed +
+ ", order=" + order +
+ ", url='" + url + '\'' +
+ '}';
+ }
+}
diff --git a/platform-http/src/main/java/org/apache/camel/example/springboot/TodoService.java b/platform-http/src/main/java/org/apache/camel/example/springboot/TodoService.java
new file mode 100644
index 0000000..a266fe4
--- /dev/null
+++ b/platform-http/src/main/java/org/apache/camel/example/springboot/TodoService.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot;
+
+import java.util.Collection;
+
+/**
+ * Service interface for managing todos.
+ */
+public interface TodoService {
+ Collection<Todo> findNotCompleted();
+
+ Collection<Todo> findCompleted();
+
+ long deleteCompleted();
+
+ Todo findById(long id);
+
+ void create(Todo todo);
+
+ Todo update(Todo todo, long id);
+
+ void deleteOne(long id);
+
+ Collection<Todo> listAll();
+}
diff --git a/platform-http/src/main/java/org/apache/camel/example/springboot/TodoServiceImpl.java b/platform-http/src/main/java/org/apache/camel/example/springboot/TodoServiceImpl.java
new file mode 100644
index 0000000..af9e2c3
--- /dev/null
+++ b/platform-http/src/main/java/org/apache/camel/example/springboot/TodoServiceImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot;
+
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+@Service("todoService")
+public class TodoServiceImpl implements TodoService {
+
+ private Map<Long, Todo> todos = new ConcurrentHashMap<>();
+ private AtomicLong counter = new AtomicLong(1);
+
+ @Override
+ public Collection<Todo> findNotCompleted() {
+ return todos.values().stream().filter(todo -> !todo.isCompleted()).collect(Collectors.toList());
+ }
+
+ @Override
+ public Collection<Todo> findCompleted() {
+ return todos.values().stream().filter(Todo::isCompleted).collect(Collectors.toList());
+ }
+
+ @Override
+ public long deleteCompleted() {
+ long completed = todos.values().stream().filter(Todo::isCompleted).count();
+
+ todos.entrySet().removeIf(entry -> entry.getValue().isCompleted());
+
+ return completed;
+ }
+
+ @Override
+ public Todo findById(long id) {
+ return todos.get(id);
+ }
+
+ @Override
+ public void create(Todo todo) {
+ todo.setId(counter.getAndIncrement());
+ todos.put(todo.getId(), todo);
+ }
+
+ @Override
+ public Todo update(Todo todo, long id) {
+ Todo persistedTodo = todos.get(id);
+ if (persistedTodo != null) {
+ persistedTodo.setCompleted(todo.isCompleted());
+ persistedTodo.setTitle(todo.getTitle());
+ persistedTodo.setOrder(todo.getOrder());
+ persistedTodo.setUrl(todo.getUrl());
+
+ return persistedTodo;
+ }
+ return null;
+ }
+
+ @Override
+ public void deleteOne(long id) {
+ todos.remove(id);
+ }
+
+ @Override
+ public Collection<Todo> listAll() {
+ return todos.values();
+ }
+}
diff --git a/platform-http/src/main/resources/application.properties b/platform-http/src/main/resources/application.properties
new file mode 100644
index 0000000..b05ed20
--- /dev/null
+++ b/platform-http/src/main/resources/application.properties
@@ -0,0 +1,22 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+# the name of Camel
+camel.springboot.name = MyCamel
+
+# to reconfigure the camel servlet context-path mapping to use /api/* instead of /camel/*
+camel.servlet.mapping.context-path=/api/*
diff --git a/pom.xml b/pom.xml
index 861f776..ac26cea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,7 @@
<module>metrics</module>
<module>opentracing</module>
<module>paho-mqtt5-shared-subscriptions</module>
+ <module>platform-http</module>
<module>pojo</module>
<module>rabbitmq</module>
<module>reactive-streams</module>