39.5. SOAP/HTTP-to-JMS Bridge Use Case

Overview

In this section, we consider a SOAP/HTTP-to-JMS bridge use case: that is, you want to create a route that transforms a synchronous operation invocation (over SOAP/HTTP) into an asynchronous message delivery (by pushing the message onto a JMS queue). In this way, it becomes possible to process the incoming operation invocations at a later time, by pulling messages off the JMS queue.
Of course, an alternative solution would be to modify the WSDL contract directly to declare the operation as OneWay, thus making the operation asynchronous. Unfortunately, it is often impractical to modify existing WSDL contracts in the real world, because this can have an impact on third-party applications.
Figure 39.2, “SOAP/HTTP-to-JMS Bridge” shows the general outline of a bridge that can transform synchronous SOAP/HTTP invocations into asynchronous JMS message deliveries.

Figure 39.2. SOAP/HTTP-to-JMS Bridge

SOAP/HTTP-to-JMS Bridge

Transforming RPC operations to One Way

As shown in Figure 39.2, “SOAP/HTTP-to-JMS Bridge”, the route for transforming synchronous SOAP/HTTP to asynchronous JMS works as follows:
  1. The WS client invokes a synchronous operation on the Camel CXF endpoint at the start of the route. The Camel CXF endpoint then creates an initial InOut exchange at the start of the route, where the body of the exchange message contains a payload in XML format.
  2. The inOnly DSL command pushes a copy of the XML payload onto a JMS queue, so that it can be processed offline at some later time.
  3. The transform DSL command constructs an immediate response to send back to the client, where the response has the form of an XML string.
  4. The Camel CXF component supports implicit type conversion of the XML string to payload format.
  5. The response is sent back to the WS client, thus completing the synchronous operation invocation.
Evidently, this transformation can only work, if the original operation invocation has no return value. Otherwise, it would be impossible to generate a response message before the request has been processed.

Creating a broker instance

You can use Apache ActiveMQ as the JMS implementation. A convenient approach to use in this demonstration is to embed the Apache ActiveMQ broker in the bridge bundle. Simply define an amq:broker element in the Spring XML file, as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
      ...
      xmlns:amq="http://activemq.apache.org/schema/core"
      ...>

  <amq:broker brokerName="CxfPayloadDemo" persistent="false">
      <amq:transportConnectors>
          <amq:transportConnector name="openwire" uri="tcp://localhost:51616"/>
          <amq:transportConnector name="vm" uri="vm:local"/>
      </amq:transportConnectors>
  </amq:broker>
  ...    
</beans>
Note
This broker instance is created with the persistent attribute set to false, so that the messages are stored only in memory.

Configuring the JMS component

Because the broker is co-located with the bridge route (in the same JVM), the most efficient way to connect to the broker is to use the VM (Virtual Machine) transport. Configure the Apache ActiveMQ component as follows, to connect to the co-located broker using the VM protocol:
<beans ...>
    ...
    <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
        <property name="brokerURL" value="vm:local"/>
    </bean>
    ...
</beans>
Note
By defining the bean with an id value of activemq, you are implicitly overriding the component associated with the endpoint URI prefix, activemq:. In other words, your custom ActiveMQComponent instance is used instead of the default ActiveMQComponent instance from the camel-activemq JAR file.

Sample SOAP/HTTP-to-JMS route

For example, you could define a route that implements the SOAP/HTTP-to-JMS bridge specifically for the updateCustomer operation from the CustomerService SEI, as follows:
<when>
    <simple>${in.header.operationName} == 'updateCustomer'</simple>
    <log message="Placing update customer message onto queue."/>
    <inOnly uri="activemq:queue:CustomerUpdates?jmsMessageType=Text"/>
    <transform>
        <constant>
        <![CDATA[
<ns2:updateCustomerResponse xmlns:ns2="http://demo.fusesource.com/wsdl/CustomerService/"/>
        ]]>
        </constant>
    </transform>
</when>

Sending to the JMS endpoint in inOnly mode

Note how the message payload is sent to the JMS queue using the inOnly DSL command instead of the to DSL command. When you send a message using the to DSL command, the default behavior is to use the same invocation mode as the current exchange. But the current exchange has an InOut MEP, which means that the to DSL command would wait forever for a response message from JMS.
The invocation mode we want to use when sending the payload to the JMS queue is InOnly (asynchronous), and we can force this mode by inserting the inOnly DSL command into the route.
Note
By specifying the option, jmsMessageType=Text, Camel CXF implicitly converts the message payload to an XML string before pushing it onto the JMS queue.

Returning a literal response value

The transform DSL command uses an expression to set the body of the exchange's Out message and this message is then used as the response to the client. Your first impulse when defining a response in XML format might be to use a DOM API, but in this example, the response is specified as a string literal. This approach has the advantage of being both efficient and very easy to program.
The final step of processing, which consists of converting the XML string to a DOM object, is performed by Apache Camel's implicit type conversion mechanism.