38.4. 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 38.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 38.2. SOAP/HTTP-to-JMS Bridge
Transforming RPC operations to One Way
As shown in Figure 38.2, “SOAP/HTTP-to-JMS Bridge”, the route for transforming synchronous SOAP/HTTP to asynchronous JMS works as follows:
- 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.
- 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. - The
transform
DSL command constructs an immediate response to send back to the client, where the response has the form of an XML string. - The Camel CXF component supports implicit type conversion of the XML string to payload format.
- 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.