Implementing a web service with a Camel route

The Camel CXF Component allows a Camel route to act as a SOAP (or REST) web service listener. This allows you to create web service frontends for other systems, including acting as a web service proxy—something that is discussed in-depth in the Web service proxying recipe.

This recipe will show you the basic steps of exposing a Camel route as a web service consumer/listener.

Getting ready

This recipe assumes that you have a project with JAX-WS artifacts created as shown in the Generating the service stubs from a WSDL recipe. To use the generated API, you need to include a dependency to that project in your build:

<dependency>
  <groupId>org.camelcookbook.examples</groupId>
  <artifactId>ws-payments-api</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

This recipe's example is based on paymentService.wsdl, whose interface has one operation, transferFunds.

<wsdl:portType name="Payment">
  <wsdl:operation name="transferFunds">
    <wsdl:input message="tns:TransferRequest"/>
    <wsdl:output message="tns:TransferResponse"/>
    <wsdl:fault name="fault" message="tns:FaultMessage"/>
  </wsdl:operation>
</wsdl:portType>

All source files for this recipe are located in the ws-camel-routes project in the camel-cookbook-web-services module. The Java code for this recipe is located in the org.camelcookbook.ws.service package. The Spring XML files are located under src/main/resources/META-INF/spring and prefixed with service.

How to do it...

There are three primary steps to expose a web service listener (consumer) using Camel:

  1. Configure the Camel CXF endpoint.
  2. Write some code that does something with the message.
  3. Write a route that consumes the message from the endpoint, routes the request to the business logic and returns a response, if applicable.

The preceding steps are performed as follows:

  1. Configure the Camel CXF endpoint.

    In the XML DSL, you will want to use the cxfEndpoint defined in the Camel CXF schema. This makes it possible for your IDE to autocomplete and validate the endpoint's parameters.

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:camel="http://camel.apache.org/schema/spring"
           xmlns:cxf="http://camel.apache.org/schema/cxf"
           xmlns:xsi=
             "http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
             http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
             http://camel.apache.org/schema/cxf
      http://camel.apache.org/schema/cxf/camel-cxf.xsd
             http://camel.apache.org/schema/spring
      http://camel.apache.org/schema/spring/camel-spring.xsd
           ">
    
      <cxf:cxfEndpoint
          id="paymentServiceEndpoint"
          address="http://localhost:1234/paymentService"
          serviceClass="org.camelcookbook.ws.payment_service.Payment"/>
    
      <!-- other stuff -->
    </beans>

    In the Java DSL, since we cannot use Spring namespace handlers (that is, cxfEndpoint), we will just create an endpoint URI String:

    final String cxfUri =
        "cxf:http://localhost:1234/paymentService?"
        + "serviceClass=" + Payment.class.getCanonicalName();
  2. Do something with the request. The easiest way to process a web service message is to create a POJO that takes the request object(s) as parameter(s), and returns the response from the method call. Since you have most likely generated the request and response artifacts from the WSDL, you can just simply use them directly in your Java code:
    import org.camelcookbook.ws.payment_service.types.TransferRequest;
    import org.camelcookbook.ws.payment_service.types.TransferResponse;
    
    public class PaymentServiceImpl {
      public TransferResponse 
          transfer(TransferRequest request) {
        TransferResponse response = new TransferResponse();
        response.setReply("OK");
        return response;
      }
    }
  3. Route the request and response. Not all web services need to return responses. Whether a response is expected is indicated by the MEP within the exchange being set to InOut, as in this example.

    In the XML DSL, you reference the endpoint you configured in step one using cxf:bean. The endpoint must be referenced within the from statement for it to be a Camel consumer, that is, in order for it to create an HTTP listener to receive the SOAP messages and feed messages into the Camel route:

    <route id="wsRoute">
      <from uri="cxf:bean:paymentServiceEndpoint"/>
      <transform>
        <simple>${in.body[0]}</simple>
      </transform>
      <log message="request = ${body}"/>
      <bean ref="paymentServiceImpl"/>
      <log message="response = ${body}"/>
    </route>

    In the Java DSL, we concatenate any additional options to our endpoint URI as we saw in step 1:

    from(cxfUri)
        .id("wsRoute")
      .transform(simple("${in.body[0]}"))
      .log("request = ${body}")
      .bean(PaymentServiceImpl.class)
      .log("response = ${body}");

Note

The Camel CXF Component, when acting as an endpoint consumer, will put an array of objects into the Camel message—specifically an array of the request parameters. For Document-style web services, there will only be a single parameter. The transform(simple("${in.body[0]}")) step will extract that single POJO request object and place it into the Camel message body for processing by the rest of the route.

How it works...

The Camel CXF Component, when used as a consumer endpoint (in the from part of the route), will start an HTTP listener internally that will receive SOAP messages when the Camel route is started. The endpoint will receive and parse SOAP messages it receives, and map them into the route as Camel exchanges.

The mapping is based on the dataFormat option of the Camel CXF Component that defaults to POJO, which will map the SOAP headers and other related connection information into exchange headers. The SOAP body will be transformed into a POJO according to the JAX-WS bindings, and placed into the message body of the exchange.

Tip

It is also possible to consume the message body as an XML document by setting the dataFormat parameter to PAYLOAD. This is useful for XPath matching and XSLT transformations.

You can also consume the entire SOAP message as an XML document by setting this parameter to MESSAGE.

When using the POJO data format, the Camel CXF endpoint consumer will create a MessageContentList, which is an array of the request parameters. This is what is placed into the exchange message body. Be aware that you will need to access the request parameters from an array, even if your web service is Document-Literal style, that is, it only contains a single parameter. This is a common source of confusion when using the Camel CXF Component.

The Java bean that processes the request could implement the generated JAX-WS web service Java interface org.camelcookbook.ws.payment_service.Payment. This would provide stronger typing to the WSDL, but it also forces you to implement all of the web service operations within a single Java class, which you may not want to do. In general, it is easier to just use the generated request and response types in your method arguments to achieve strong typing.

There's more...

The operation name of the invoked web service will be placed into an exchange header called operationName. You can use this header to correctly route and process the request. We will see an example of this in the Providing multiple web service operations within a single route recipe.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.106.237