Chapter 5. Deploying an Apache Camel WS Endpoint

Abstract

This tutorial describes how to deploy an Apache CXF Web services endpoint in a WAR file, where the Web service endpoint is implemented by binding to an Apache Camel route using the Camel CXF component.

5.1. Apache Camel CXF Example

Overview

Figure 5.1, “Camel CXF Example Deployed in a Web Server” gives an overview of the Camel CXF example deployed in a Web server, which lets you see how the Web service's URL is constructed from settings at different configuration layers. The Web server's host and port, the WAR file name, the url-pattern setting from web.xml, and the URI of the Camel CXF endpoint are combined to give the URL, http://localhost:8080/camel-example-cxf-tomcat/webservices/incident.

Figure 5.1. Camel CXF Example Deployed in a Web Server

camel-example-cxf-tomcat example

The code for this example is available from the standard Apache Camel distribution, under the examples/camel-example-cxf-tomcat directory. For details of how to install the Apache Camel distribution, see the section called “Install Apache Camel”.

Camel CXF component

The Camel CXF component binds an Apache CXF endpoint to a Camel route. That is, the Camel CXF endpoint itself is a fully-fledged Apache CXF Web service with all of the (potentially very complex) configuration options that are available from Apache CXF (including SSL security, WS-Security, and other WS-* standards). In contrast to the usual case, where you would bind the WS endpoint to a Java class (for example, using the JAX-WS binding), the Camel CXF component binds the WS endpoint to a Camel route, so that incoming SOAP message are encapsulated in a Camel Exchange object, and can then propagate through the route.
To create a Camel CXF endpoint in a Camel route, define a CXF endpoint URI with either of the following syntaxes:
cxf:Address[?Options]
Specifies the WSDL endpoint address and a (potentially large) number of options to configure the endpoint.
cxf:bean:BeanID[?Options]
References a bean with the ID, BeanID, defined using the cxf:cxfEndpoint element (where the cxf prefix is bound to the http://camel.apache.org/schema/cxf namespace). The advantage of this approach is that all of the configuration complexity is encapsulated in the bean. Typically, this means that very few options (or none) need to be specified on the endpoint URI.
Note
The cxf:cxfEndpoint element, which binds a WS endpoint to a Camel route, should not be confused with the jaxws:endpoint element, which binds a WS endpoint directly to a Java class.

More about the Camel CXF component

For more details about the Camel CXF component, please consult the following documents from the JBoss Fuse library:
  • Web Services and Routing with Camel CXF
  • The CXF chapter from the EIP Component Reference.

web.xml file

To deploy the Apache Camel CXF example, you must provide a properly configured web.xml file. In the camel-example-cxf-tomcat project, the web.xml file is stored at the following location:
camel-example-cxf-tomcat/src/main/webapp/WEB-INF/web.xml

Example 5.1. web.xml File for the camel-example-cxf-tomcat Example

<?xml version="1.0" encoding="ISO-8859-1"?>
<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>

	<!-- CXF servlet -->
	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
		<!-- If you want to leverage the Servlet3's async feature in Tomcat, 
		 please enable this feature 
		<async-supported>true</async-supported>
		-->
	</servlet>

	<!-- all our webservices are mapped under this URI pattern -->
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/webservices/*</url-pattern>
	</servlet-mapping>

</web-app>
The key settings in the preceding web.xml file are:
servlet/servlet-class
Specifies the org.apache.cxf.transport.servlet.CXFServlet class, which implements a special servlet that enables you to deploy Apache CXF WS endpoints.
servlet-mapping/url-pattern
Determines which URLs are routed to this servlet. In general, the servlet URL has the following form:
http://Host:Port/WARFileName/URLPattern
Where the base URL, http://Host:Port, is determined by the configuration of the Web server, the WARFileName is the root of the WARFileName.war WAR file, and the URLPattern is specified by the contents of the url-pattern element.
Assuming that the Web server port is set to 8080, the camel-example-cxf-tomcat example servlet will match URLs of the following form:
http://localhost:8080/camel-example-cxf-tomcat/webservices/*
listener/listener-class
This element launches a Spring container.
context-param
This element specifies the location of the Spring XML file, camel-config.xml, in the WAR. The Spring container will read this parameter and load the specified Spring XML file, which contains the definition of the Camel route.
Note
Strictly speaking, it is not absolutely necessary to create a Spring container explicitly using the listener-class element here, because the CXFServlet class already creates its own Spring container. If you put the Spring XML file in the location expected by the CXFServlet class (that is, WEB-INF/cxf-servlet.xml) instead of the location used by this example (that is, WEB-INF/classes/camel-config.xml), you could remove the Spring container settings from this web.xml file.

Spring XML file

The Spring XML file for this example, camel-config.xml, contains the following XML code:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>

  <import resource="classpath:META-INF/cxf/cxf.xml"/>

  <bean id="myRoutes" class="org.apache.camel.example.cxf.CamelRoute"/>

  <camelContext xmlns="http://camel.apache.org/schema/spring">
    <routeBuilder ref="myRoutes"/>
  </camelContext>

</beans>
In this example, the Spring XML file is used just as a convenient mechanism to bootstrap the Camel context. The XML syntax is used to create the Camel context, but the code for the RouteBuilder class is defined in the Java class, org.apache.camel.example.cxf.CamelRoute.
Note
The resource import in this file was required in earlier versions of Apache CXF, in order to import some standard, boilerplate definitions. But in recent versions of Apache CXF, this import is not required, and you can safely remove it from the Spring XML file.

Camel route class

Example 5.2, “Route Definitions in the CamelRoute Class” shows the Camel routes for this example, defined using the Java DSL.

Example 5.2. Route Definitions in the CamelRoute Class

// Java
package org.apache.camel.example.cxf;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.example.cxf.incident.IncidentService;
import org.apache.camel.example.cxf.incident.InputReportIncident;
import org.apache.camel.example.cxf.incident.OutputReportIncident;
import org.apache.camel.example.cxf.incident.OutputStatusIncident;

// this static import is needed for older versions of Camel than 2.5
// import static org.apache.camel.language.simple.SimpleLanguage.simple;

public class CamelRoute extends RouteBuilder {

    // CXF webservice using code first approach
    private String uri = "cxf:/incident?serviceClass=" + IncidentService.class.getName();

    @Override
    public void configure() throws Exception {
        from(uri)
            .to("log:input")
            // send the request to the route to handle the operation
            // the name of the operation is in that header
            .recipientList(simple("direct:${header.operationName}"));

        // report incident
        from("direct:reportIncident")
            .process(new Processor() {
                public void process(Exchange exchange) throws Exception {
                    // get the id of the input
                    String id = exchange.getIn().getBody(InputReportIncident.class).getIncidentId();

                    // set reply including the id
                    OutputReportIncident output = new OutputReportIncident();
                    output.setCode("OK;" + id);
                    exchange.getOut().setBody(output);
                }
            })
            .to("log:output");

        // status incident
        from("direct:statusIncident")
            .process(new Processor() {
                public void process(Exchange exchange) throws Exception {
                    // set reply
                    OutputStatusIncident output = new OutputStatusIncident();
                    output.setStatus("IN PROGRESS");
                    exchange.getOut().setBody(output);
                }
            })
            .to("log:output");
    }
}
The most important feature of this class is the Camel CXF endpoint URI, which appears at the start of the first route (in the from(uri) DSL command). The Camel CXF endpoint is defined using the following endpoint URI:
cxf:/incident?serviceClass=org.apache.camel.example.cxf.incident.IncidentService
Where we have substituted the literal name of the IncidentService class in this URI. The relative path, /incident, defines the tail of the servlet URL for this Web service. Hence, the full servlet URL for the Web service is the following:
http://localhost:8080/camel-example-cxf-tomcat/webservices/incident
The serviceClass option specifies the name of the Service Endpoint Interface (SEI) for this Web service. By default, the CXF endpoint is set up to use the POJO mode, using the SEI to check the syntax of incoming messages.