Apache Camel is an open source integration framework that allows you to integrate technologically diverse systems using a large library of components. A common use-case is to service HTTP based endpoints. Those of course come in several flavors and there is quite a choice in components to use.
In this blog post I’ll take a look at what is available and how they differ with respect to flexibility to define multiple hosts, ports and URLs to host from using a single CamelContext / Spring Boot application. Depending on your use-case you will probably be using one of these. You can find my sample project here.
Components
REST DSL
The REST DSL is not an actual component but can be considered a wrapper for several components. It has integration with for example the Swagger module to generate documentation. It uses a RestConfiguration which can only occur once in a CamelContext (when using Spring Boot in combination with Apache Camel). The RestConfiguration specifies things like the base URL, the port and the component used as consumer / HTTP server.
Although easy to use, it is also limiting in that only a single RestConfiguration can be used within a CamelContext and that using multiple CamelContexts within a single application is not supported (see here). Thus this will not allow you to host services on different ports from the same Spring Boot / Apache Camel application. Also it is not allowed to define the same base path for different services in two different component-routes. You can run on a different port then the base Apache Camel / Spring Boot servlet engine though.
restConfiguration().component("netty-http").host("0.0.0.0").port(8084);
rest("/url1").get().id("test_route1").to("log:dummylog");
restConfiguration().component("netty-http").host("0.0.0.0").port(8085);
rest("/url2").get().id("test_route2").to("log:dummylog");
A small example of using the REST DSL above. Mind that both services will be run on port 8085 since there is only one restConfiguration! The REST DSL is powerful since it wraps several other components. If I want to switch to for example jetty instead of netty-http, I only have to change the component specification in the REST configuration and no other code (if I made sure not to use implementation specific features of the different components). You can also externalize the component specification to make switching your HTTP server as simple as changing a property file.
You can find the documentation here and an example on how to use it here.
REST
The REST component is quite powerful. It allows you to easily expose REST endpoints using various other components which implement the org.apache.camel.spi.RestConsumerFactory such as the http, undertow, servlet, netty-http, spark-rest and jetty. Also it has a flexible URI syntax. It integrates tightly with the REST DSL.
If you do not want to use the RestConfiguration class, you can directly configure the component from the CamelContext. The RestComponent however does not have a setPort method. I could not find a quick way to create multiple rest components running on different ports. This is similar as to using REST DSL.
RestComponent sc = getContext().getComponent("rest",RestComponent.class);
sc.setConsumerComponentName("jetty");
from("rest:get:/:url1").id("test_route1").to("log:dummylog");
Mind that you explicitly need to specify the component to use if it is not present in the RestConfiguration and you have multiple components providing the RestConsumerFactory on your classpath. Else you will encounter the following error:
Caused by: java.lang.IllegalArgumentException: Multiple RestConsumerFactory found on classpath. Configure explicit which component to use.
HTTP
The camel-http component (in Apache Camel 2.x use camel-http4 instead) allows you to consume HTTP services. It does not allow you to host services with. For that the jetty component is recommended.
Jetty
You can use the Jetty component to create multiple routes with the same or different ports and specify different URLs for the same port in different routes.
The below example will work.
from("jetty:http://0.0.0.0:8083/url1").id("test_route1").to("log:dummylog");
from("jetty:http://0.0.0.0:8083/url2").id("test_route2").to("log:dummylog");
Jetty has the challenge that Jetty libraries are regular inhabitants of several application servers (at least WebLogic). If you want the classes from your deployment to be used instead of the (often outdated) application server versions, you might need to manipulate dependencies and class loading (create web.xml files, deployment descriptors, etc).
Servlet
The servlet component allows you to host services at different endpoints, however you don’t have the option to configure different ports or URLs outside of the path specified in the property camel.component.servlet.mapping.context-path and server-port in application.properties when using the Apache Camel Servlet Spring Boot starter. You can not easily run on a different port then the base Apache Camel / Spring Boot servlet engine.
//Both OK, however path below camel.component.servlet.mapping.context-path and uses the same port
from("servlet:/url1").id("test_route1").to("log:dummylog");
from("servlet:/url2").id("test_route2").to("log:dummylog");
Netty-http
The Netty-http component facilitates HTTP transport using the Netty component. The Netty component is low-level and socket based while netty-http allows you to easily do HTTP with Netty. Netty-http allows you to use the same port in different routes as long as the NettyServerBootstrapConfiguration is the same. This means the configuration of the route is the same; they use the same parameters in the URI. This creates in my opinion a lot of flexibility.
In the below example you can see that hosting on the same port with different URLs works without challenges.
The relevant code:
from("netty-http:http://0.0.0.0:8083/url1").id("test_route1").to("log:dummylog");
from("netty-http:http://0.0.0.0:8083/url2").id("test_route2").to("log:dummylog");
Undertow
The Undertow component allows you to host HTTP and WebSocket endpoints.
from("undertow:http://0.0.0.0:8083/myapp1").id("test_route1").to("log:dummylog");
from("undertow:http://0.0.0.0:8084/myapp2").id("test_route2").to("log:dummylog");
Using Undertow has the benefit of not conflicting with application server libraries on WebLogic. An example of how to use Undertow and Netty to run an Apache Camel / Spring Boot application on WebLogic can be found here. Wildfly also uses Undertow as HTTP server and you might encounter issues there.
Spark REST
The Spark REST component and Spark Framework have nothing to do with Apache Spark!
If you want to interface with Apache Spark, you should use the Spark component. The Spark REST component allows you to use Spark Framework. This is a micro framework for creating web applications in Kotlin and Java 8. Mind that the support for Java 8 in Apache Camel 3.x is best effort; you should be running Apache Camel 3.x on Java 11!
It uses Jetty and provides an alternative syntax to the REST DSL. You can however use the REST DSL instead of Spark but still use the Spark REST component. In that case you can still obtain the raw Spark request by using the getRequest method in org.apache.camel.component.sparkrest.SparkMessage and still do Spark specific things with that.
Supplying the port option in the URI did not work:
Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: spark-rest://get:url1?port=8083 due to: There are 1 parameters that couldn't be set on the endpoint. Check the uri if the parameters are spelt correctly and that they are properties of the endpoint. Unknown parameters=[{port=8083}]
I discovered the port is a property of the Component instance and not the Endpoint. The Component instance can be obtained from the CamelContext. However, since there is a single Component instance of spark-rest in the CamelContext, you can only supply a single port. Using the RestConfiguration for this of course does not help either since there is also only one instance of that class available in the CamelContext. A workaround for this is adding more components to the CamelContext with different names and a different configuration.
SparkComponent sc = getContext().getComponent("spark-rest",SparkComponent.class);
sc.setPort(8083);
SparkConfiguration sc2config = sc.getSparkConfiguration();
SparkComponent sc2 = new SparkComponent();
sc2.setSparkConfiguration(sc2config);
sc2.setPort(8084);
getContext().addComponent("spark-rest2",sc2);
from("spark-rest://get:url1").id("test_route1").to("log:dummylog");
from("spark-rest2://get:url2").id("test_route2").to("log:dummylog");
This way you can run on multiple ports/URLs using a distinct Component instance per port. This code is however Spark specific and if you want to switch to for example plain Jetty, you have to change it. Also there is the application server challenge since it uses Jetty.
Finally
Summary
Please mind there are many other considerations for choosing a specific component. You can think of specific features like WebSocket support, performance, security related features, blocking (jetty, spark, servlet) or non-blocking (netty, undertow) nature, resource consumption, etc.
The below table summarizes my findings on components which can be used to run as Apache Camel HTTP server within Spring Boot. Red means no (no no expected issues is a good thing ;), green means yes, yellow means ‘it depends’ or ‘not easily’. Undertow, Jetty, Netty come out quite nicely as being a flexible components in respect to hosting on different ports and URLs. They can be used with the REST DSL, however when you do, you’ll loose this flexibility. I can’t recommend using servlets. They are not flexible and you can expect challenges on application servers. When comparing Spark Framework and the REST DSL for REST specific features, I’d go for the more mainstream REST DSL which can be used by several components including Spark Framework. When not using application servers which use Undertow themselves such as Wildfly, Undertow appears to be a good choice.
Apache Camel 2.x vs Apache Camel 3.x
The differences between Camel 2.x and Camel 3.x are well described in the migration guide. Two things I directly encountered are listed below.
groupId of artifacts
In Apache Camel 2.x most of the Spring Boot related artifacts such as the starters are present under groupId org.apache.camel. For Apache Camel 3.x the documentation specifies org.apache.camel.springboot as the groupId. Here only artifacts for the 3.x version are present.
netty4-http and camel-http4
Apache Camel 2.x has netty-http which is deprecated and netty4-http which is a newer version of the component which should be the one to use. For the camel-http component it is similar. Don’t use it, but use camel-http4. In Apache Camel 3.x however there is is no ‘4’ version of both components and just netty-http and http. Thus for Apache Camel 2.x you should use netty4-http and camel-http4 while for Apache Camel 3.x you should use netty-http and http. This is also something to mind when migrating.