Multiple Camel Servlets

Background

I’ve been having some troubles getting the Camel REST DSL working when deploying on an RedHat Jboss EAP 6.4 server, using Fuse 6.2.1. I ended up using Camel Spring for my wiring, just to keep the option open to easily migrate to a container platform.

Our stack in this looks like:

  • Jboss EAP 6.4
  • Fuse 6.2.1
  • Camel 2.15.1

The Setup

Lets start by deploying just a single Camel route with a REST endpoint, using the REST DSL.

In order to bootstrap camel-spring, I’ve added a web.xml to my WEB-INF:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- XML file -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--Camel servlet -->
    <servlet>
        <servlet-name>CamelServlet</servlet-name>
        <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Camel servlet mapping -->
    <servlet-mapping>
        <servlet-name>CamelServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

The context-param element links to the Camel applicatonContext.xml. This file is bootstrapped by the ContextLoader listener.

In order to use the Camel REST DSL, we need to define a servlet that we can use to bind our rest configuration to. The Servlet itself is defined in the servlet element. The servlet is mapped to any urls in the servlet-mapping.

Next up, we create a jboss-web.xml file, also in WEB-INF:

<jboss-web>
    <context-root>/root</context-root>
</jboss-web>

And then create a Router class. This class should contain a call to the restConfiguration method in the configure method(). There we need to bind the rest configuration to the camel servlet component.

@Component
public class MyRouter extends RouteBuilder {

    @Override
    public void configure() {

        restConfiguration("servlet");

        rest("/")
                .get().produces("application/json").to("direct:start");

        from("direct:start")
                .setBody(constant("{'message': 'Hello world from deployment one!'}"));
    }

}

When deploying this application to EAP, we are greeted by the following confirmation in our logging:

11:53:46,853 INFO  [org.apache.camel.component.servlet.CamelHttpTransportServlet] (ServerService Thread Pool -- 83) Initialized CamelHttpTransportServlet[name=CamelServlet, contextPath=/root]

The problem

One deploying is fine. The problem occurs though when deploying a second application with the same configuration:

"WFLYCTL0080: Failed services" => {"jboss.undertow.deployment.default-server.default-host./one" => "javax.servlet.ServletException: Duplicate ServletName detected: CamelServlet. Existing: CamelHttpTransportServlet[name=CamelServlet] This: CamelHttpTransportServlet[name=CamelServlet]. Its advised to use unique ServletName per Camel application.
    Caused by: javax.servlet.ServletException: Duplicate ServletName detected: CamelServlet. Existing: CamelHttpTransportServlet[name=CamelServlet] This: CamelHttpTransportServlet[name=CamelServlet]. Its advised to use unique ServletName per Camel application."}}

As we are binding our Servlet on EAP, the application server will detect two Servlets with the same name. The fix for this is quite easy.

The solution

We can resolve this issue by simply changing the name of our second servlet in our web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- XML file -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--Camel servlet -->
    <servlet>
        <servlet-name>SecondCamelServlet</servlet-name>
        <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Camel servlet mapping -->
    <servlet-mapping>
        <servlet-name>SecondCamelServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Url pattern and mapping can be the same, we only need to change the name of the Servlet.

This will deploy just fine, but you will notice that you cannot acutally call your endpoint. No errors, nothing.

To fix this, we need to change our restConfiguration to point to a specific Servlet. This can be set uring an endpointProperty called “servletName”. This looks like:

@Component
public class MyRouter extends RouteBuilder {

    @Override
    public void configure() {

        restConfiguration("servlet")
                .endpointProperty("servletName", "SecondCamelServlet");


        rest("/")
                .get().produces("application/json").to("direct:start");

        from("direct:start")
                .setBody(constant("{'message': 'Hello world from deployment one!'}"));
    }

}

This will make sure that your Rest configuration binds to the newly named SecondCamelServlet.

Please not that this is only an issues when running on EAP 6.4 with Fuse 6.2.1. EAP 7+ with Fuse 7+ will resolve your Servlets automatically, without needing to set the property. This is only needed when you want to define multiple servlets and want to bind specific rest configurations to specific Servlets.