Red Hat Training

A Red Hat training course is available for Red Hat Fuse

Chapter 146. SERVLET

Servlet Component

The servlet: component provides HTTP-based endpoints for consuming HTTP requests that arrive at a HTTP endpoint and this endpoint is bound to a published Servlet.
Maven users will need to add the following dependency to their pom.xml for this component:
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-servlet</artifactId>
    <version>x.x.x</version>
    <\!-\- use the same version as your Camel core version \-->
</dependency>

Camel on EAP deployment

This component is supported by the Camel on EAP (Wildfly Camel) framework, which offers a simplified deployment model on the Red Hat JBoss Enterprise Application Platform (JBoss EAP) container. For details of this model, see chapter "Apache Camel on JBoss EAP" in "Deploying into a Web Server".

URI format

servlet://relative_path[?options]
You can append query options to the URI in the following format, ?option=value&option=value&...

Options

Name Default Value Description
httpBindingRef null Reference to an org.apache.camel.component.http.HttpBinding in the Registry. A HttpBinding implementation can be used to customize how to write a response.
httpBinding null Camel 2.16: Reference to an org.apache.camel.component.http.HttpBinding in the Registry. A HttpBinding implementation can be used to customize how to write a response.
matchOnUriPrefix false Whether or not the CamelServlet should try to find a target consumer by matching the URI prefix, if no exact match is found.
servletName CamelServlet Specifies the servlet name that the servlet endpoint will bind to. If there is no servlet name specified, the servlet endpoint will be bind to first published Servlet.
httpMethodRestrict null Camel 2.11: (Consumer only) Used to only allow consuming if the HttpMethod matches, such as GET/POST/PUT, and so on. From Camel 2.15 onwards, multiple methods can be specified, separated by a comma.

Message Headers

Apache Camel will apply the same Message Headers as the HTTP component.
Apache Camel will also populate allrequest.parameter and request.headers. For example, if a client request has the URL, http://myserver/myserver?orderid=123, the exchange will contain a header named orderid with the value 123.

Usage

You can only consume from endpoints generated by the Servlet component. Therefore, it should only be used as input into your Apache Camel routes. To issue HTTP requests against other HTTP endpoints, use the HTTP Component

Putting Camel JARs in the app server boot classpath

If you put the Camel JARs such as camel-core, camel-servlet, etc. in the boot classpath of your application server (eg usually in its lib directory), then mind that the servlet mapping list is now shared between multiple deployed Camel application in the app server.
Mind that putting Camel JARs in the boot classpath of the application server is generally not best practice!
So in those situations you must define a custom and unique servlet name in each of your Camel application, eg in the web.xml define:
<servlet>
  <servlet-name>MyServlet</servlet-name>
  <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>MyServlet</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>
And in your Camel endpoints then include the servlet name as well
<route>
  <from uri="servlet://foo?servletName=MyServlet"/>
  ...
</route>
From Camel 2.11 onwards Camel will detect this duplicate and fail to start the application. You can control to ignore this duplicate by setting the servlet init-parameter ignoreDuplicateServletName to true as follows:
  <servlet>
    <servlet-name>CamelServlet</servlet-name>
    <display-name>Camel Http Transport Servlet</display-name>
    <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
    <init-param>
      <param-name>ignoreDuplicateServletName</param-name>
      <param-value>true</param-value>
    </init-param>
  </servlet>
But its strongly advised to use unique servlet-name for each Camel application to avoid this duplication clash, as well any unforeseen side-effects.

Sample

Important
From Camel 2.7 onwards its easier to use Servlet in Spring web applications. See Servlet Tomcat Example for details.
In this sample, we define a route that exposes a HTTP service at http://localhost:8080/camel/services/hello. First, you need to publish the CamelHttpTransportServlet through the normal Web Container, or OSGi Service. Use the Web.xml file to publish the CamelHttpTransportServlet as follows:
<web-app>

  <servlet>
    <servlet-name>CamelServlet</servlet-name>
    <display-name>Camel Http Transport Servlet</display-name>
    <servlet-class>
        org.apache.camel.component.servlet.CamelHttpTransportServlet
    </servlet-class>
    
  </servlet>

  <servlet-mapping>
    <servlet-name>CamelServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>

</web-app>
Then you can define your route as follows:
from("servlet:///hello?matchOnUriPrefix=true").process(new Processor() {
    public void process(Exchange exchange) throws Exception {                    
        String contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);
        String path = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);
        path = path.substring(path.lastIndexOf("/"));

        assertEquals("Get a wrong content type", CONTENT_TYPE, contentType);
        // assert camel http header
        String charsetEncoding = exchange.getIn().getHeader(Exchange.HTTP_CHARACTER_ENCODING, String.class);
        assertEquals("Get a wrong charset name from the message heaer", "UTF-8", charsetEncoding);
        // assert exchange charset
        assertEquals("Get a wrong charset naem from the exchange property", "UTF-8", exchange.getProperty(Exchange.CHARSET_NAME));
        exchange.getOut().setHeader(Exchange.CONTENT_TYPE, contentType + "; charset=UTF-8");                        
        exchange.getOut().setHeader("PATH", path);
        exchange.getOut().setBody("<b>Hello World</b>");
    }
});
Specify the relative path for camel-servlet endpoint
Since we are binding the Http transport with a published servlet, and we don't know the servlet's application context path, the camel-servlet endpoint uses the relative path to specify the endpoint's URL. A client can access the camel-servlet endpoint through the servlet publish address: ("http://localhost:8080/camel/services") + RELATIVE_PATH("/hello").

Sample when using Spring 3.x

The standalone Apache Camel package contains a demonstration of how to deploy the Servlet component in the Tomcat Web container. The demonstration is located in the examples/camel-example-servlet-tomcat directory. When deploying a Servlet component in the Web container, it is necessary to create a Spring application context explicitly by creating a Spring ContextLoaderListener instance in the WEB-INF/web.xml file.
For example, to create a Spring application context that loads Spring definitions (including the camelContext and route definitions) from the camel-config.xml file, define a web.xml file as follows:
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>My Web Application</display-name>

    <!-- location of spring xml files -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:camel-config.xml</param-value>
    </context-param>

    <!-- the listener that kick-starts Spring -->
    <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>/camel/*</url-pattern>
    </servlet-mapping>

</web-app>

Sample when using Spring 2.x

When using the Servlet component in a Camel/Spring application it's often required to load the Spring ApplicationContext after the Servlet component has started. This can be accomplished by using Spring's ContextLoaderServlet instead of ContextLoaderListener. In that case you'll need to start ContextLoaderServlet after CamelHttpTransportServlet like this:
<web-app>
  <servlet> 
        <servlet-name>CamelServlet</servlet-name> 
        <servlet-class> 
            org.apache.camel.component.servlet.CamelHttpTransportServlet 
        </servlet-class> 
        <load-on-startup>1</load-on-startup> 
  </servlet> 
  <servlet> 
        <servlet-name>SpringApplicationContext</servlet-name> 
        <servlet-class> 
            org.springframework.web.context.ContextLoaderServlet 
        </servlet-class> 
        <load-on-startup>2</load-on-startup> 
  </servlet> 
<web-app>

Sample when using OSGi

From Camel 2.6.0, you can publish the CamelHttpTransportServlet as an OSGi service with help of SpringDM like this.
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    
    <bean id="camelServlet" class="org.apache.camel.component.servlet.CamelHttpTransportServlet"/>
    
    <!-- 
        Enlist it in OSGi service registry 
        This will cause two things:
        1) As the pax web whiteboard extender is running the CamelServlet will
           be registered with the OSGi HTTP Service
        2) It will trigger the HttpRegistry in other bundles so the servlet is
           made known there too
    -->
    <service ref="camelServlet">
        <interfaces>
            <value>javax.servlet.Servlet</value>
            <value>org.apache.camel.component.http.CamelServlet</value>
        </interfaces>
        <service-properties>
            <entry key="alias" value="/camel/services" />
            <entry key="matchOnUriPrefix" value="true" />
            <entry key="servlet-name" value="CamelServlet"/>
        </service-properties>
    </service>

</blueprint>
Then use this service in your camel route like this:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:camel="http://camel.apache.org/schema/blueprint">

    <reference id="servletref" interface="org.apache.camel.component.http.CamelServlet">
          <reference-listener bind-method="register" unbind-method="unregister">
             <ref component-id="httpRegistry"/>
          </reference-listener>
    </reference>
    
    <bean id="httpRegistry" class="org.apache.camel.component.servlet.DefaultHttpRegistry"/>
        
    <bean id="servlet" class="org.apache.camel.component.servlet.ServletComponent">
        <property name="httpRegistry" ref="httpRegistry" />
    </bean>

    <bean id="servletProcessor" class="org.apache.camel.itest.osgi.servlet.ServletProcessor" />

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <!-- notice how we can use the servlet scheme which is that osgi:reference above -->
            <from uri="servlet:///hello"/>
            <process ref="servletProcessor"/>
        </route>
    </camelContext>

</blueprint>
Alternatively - pre Camel 2.6 - you can use an Activator to publish the CamelHttpTransportServlet on the OSGi platform
import java.util.Dictionary;
import java.util.Hashtable;

import org.apache.camel.component.servlet.CamelHttpTransportServlet;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.osgi.context.BundleContextAware;

public final class ServletActivator implements BundleActivator, BundleContextAware {
    private static final transient Logger LOG = LoggerFactory.getLogger(ServletActivator.class);
    private static boolean registerService;
    
    /**
     * HttpService reference.
     */
    private ServiceReference httpServiceRef;
    
    /**
     * Called when the OSGi framework starts our bundle
     */
    public void start(BundleContext bc) throws Exception {
        registerServlet(bc);
    }

    /**
     * Called when the OSGi framework stops our bundle
     */
    public void stop(BundleContext bc) throws Exception {
        if (httpServiceRef != null) {
            bc.ungetService(httpServiceRef);
            httpServiceRef = null;
        }
    }
    
    protected void registerServlet(BundleContext bundleContext) throws Exception {
        httpServiceRef = bundleContext.getServiceReference(HttpService.class.getName());
        
        if (httpServiceRef != null && !registerService) {
            LOG.info("Register the servlet service");
            final HttpService httpService = (HttpService)bundleContext.getService(httpServiceRef);
            if (httpService != null) {
                // create a default context to share between registrations
                final HttpContext httpContext = httpService.createDefaultHttpContext();
                // register the hello world servlet
                final Dictionary<String, String> initParams = new Hashtable<String, String>();
                initParams.put("matchOnUriPrefix", "false");
                initParams.put("servlet-name", "CamelServlet");
                httpService.registerServlet("/camel/services", // alias
                    new CamelHttpTransportServlet(), // register servlet
                    initParams, // init params
                    httpContext // http context
                );
                registerService = true;
            }
        }
    }

    public void setBundleContext(BundleContext bc) {
        try {
            registerServlet(bc);
        } catch (Exception e) {
            LOG.error("Cannot register the servlet, the reason is " + e);
        }
    }

}