Wednesday, June 29, 2011

Camel Integration with Oracle WebLogic JMS

This post is similar to a previous post Camel Integration with WebSphere MQ. However, this time I will describe my experience with implementing a Camel route that integrates with WebLogic JMS. The Camel route runs inside Servicemix as an OSGI bundle. I used the Enterprise version of Servicemix from FuseSource available here:
http://fusesource.com/downloads/

This use-case was developed to show how to achieve interoperability with WebLogic.
You can download the example code here,

You can extract this file and, using Eclipse, import as an "existing Maven project". You will need to have the M2Eclipse plugin installed.
Overview
The following diagram shows what is happening in the use case created to demonstrate interoperability with WebLogic.







The use case consists of a Camel route (deployed inside Servicemix) that receives an HTTP message from an external client. The arrival of the HTTP message triggers the route. The route is pretty simple. It simply takes the payload of the HTTP message and drops it onto a WebLogic message queue.

The HTTP message received (that triggers the route) is a two-way (request/response.) The message exchange pattern (MEP) for any route is generally determined by the first (consumer) endpoint in a route. Therefore the MEP for this particular route is request/response.

In this route, the JMS producer is responsible for placing the message on the pre-configured WebLogic JMS queue. Because the route is request/response, the JMS producer will by default wait on a response to arrive on the response queue. This response queue is determined by whatever queue name is set in the outgoing JMS message’s JMSReplyTo header. The JMSReplyTo header is a standard-based JMS message header that indicates which address (queue) the JMS consumer that receives the message, should reply to.

In some cases the replyTo queue could be a temporary queue set up (per-message) by Camel on the fly to listen for reply (dynamic replyTo). In other cases, the replyTo queue will be a well-known location (static replyTo.) In the current use-case the replyTo queue is a well-known (static) location. This follows a pattern common to many JMS applications.

To make it easy to test, this example creates a secondary route inside WebLogicRoute.java. that serves as a “mock” server. This route listens on the “test” queue and transforms the message (simply writes request to response) and places response on the replyTo queue.
Implementation
The following shows the Camel code for this route.

from("jetty:http://0.0.0.0:8888/placeorder")
.to(“wmq:queue:test?replyTo=testResponse&useMessageIDAsCorrelationID=true");


The code simply listens for incoming HTTP messages (via the camel Jetty component) and forwards the payload to a WebLogic queue specified by “wmq:queue:test”. (Note the “wmq:” refers to the bean ID of JMSComponent in the Spring configuration file.) The route sets the value of the “JMSreplyTo” header using an option on the outgoing queue URI. Thus the route, after sending to the “test” queue, will wait on a response on the queue specified in the ‘replyTo’ option. In this case the route will listen for a response on “testResponse”. Camel will by default use a message selector to identify the response message, searching for the response message that has a corrrelationId that matching the messageId of the original message. In this way, Camel handles the message correlation for you without additional code. The response then gets converted and sent back to the original client via HTTP.
A secondary route exist merely to “mock” a server and will receive requests and post replies. Typically this will be done by an external process but for our purposes this “mock” service makes the route self-contained and easier to test. Additionally, this secondary route shows just how easy it is to pull messages off a queue, process them, and post replies. The code for this secondary “mock” service is shown below:

// This can be used to "Mock" Server
from(“wmq:queue:test?useMessageIDAsCorrelationID=true")        
        .to("myTransform");

Should you decide to use an external Service to pull requests off the “test” queue you can simply comment out this secondary route.
Spring Configuration
The Spring configuration for WebLogic, used in this example route is shown below:

<beans xmlns="http://www.springframework.org/schema/beans"
       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/spring
                 http://camel.apache.org/schema/spring/camel-spring.xsd">

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

    <bean id="weblogicRoute" class="com.fusesource.example.camel.WebLogicRoute">
         <!-- This is the "request" queue -->
        <property name="requestQueue" value="test"/>
         <!-- This is the "response" queue -->   
        <property name="responseQueue" value="testResponse"/>
    </bean>

         <bean id="myTransform" class="com.fusesource.example.camel.MyTransform"/>

        <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
            <property name="environment">
                <props>
                    <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
                    <prop key="java.naming.provider.url">t3://localhost:7001</prop>
                    <prop key="java.naming.security.principal">weblogic</prop>
                    <prop key="java.naming.security.credentials">password1</prop>
                </props>
            </property>
        </bean>

        <bean id="jndiFactoryBean" class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiName" value="jms/connectionFactory"/>
            <property name="jndiTemplate" ref="jndiTemplate"/>
            <property name="lookupOnStartup" value="false"/>
            <property name="proxyInterface" value="javax.jms.ConnectionFactory"/>
        </bean>

        <bean id="jndiDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
            <property name="jndiTemplate" ref="jndiTemplate"/>
        </bean>

          <bean id="jmsConfiguration" class="org.apache.camel.component.jms.JmsConfiguration">
            <property name="connectionFactory" ref="jndiFactoryBean"/>
            <property name="destinationResolver" ref="jndiDestinationResolver"/>
        </bean>

        <bean id="wmq" class="org.apache.camel.component.jms.JmsComponent">
               <property name="configuration" ref="jmsConfiguration" />
        </bean>

</beans>

WebLogic Setup
As a JMS broker, WebLogic is somewhat picky about how things are set up. You are responsible for setting up a server, a domain, a jms module, a subdeployment and a connection factory and a queue.
1)     Set up a server for the following t3://localhost:7001.
2)     Set up a connection factory and give jms/connectionFactory as its JNDI name.
3)     Set up a queue and give “test” as its JNDI name.
4)     Set up a queue and give “testResponse” as its jndi name.
5)     Make sure Admin Server for the Domain you just created is up before starting the example Camel route.
A good reference guide for setting up these things inside WebLogic using Oracle Admin Server Console is the following reference:
Making WebLogic Dependency (JAR) available to Maven

When implementing a Camel solution to interoperate with WebLogic MQ, there is a required WebLogic JMS specific client library needed. For the purpose of this example, this library was obtained from an installation of Oracle WebLogic Server 11g for Windows.
The specific WebLogic library required at run-time is “wlthint3client.jar”.
To install the library into your local Maven repo, and thus available to Maven build (and Servicemix), go to your WebLogic installation directory and install wlthint3client using appropriate Maven command. (Note: this library has been included for your convenience in the \lib folder of this example.)
For example, if you have installed WebLogic in  c:\Oracle, the command to install the weblogic client jar into your local maven repo would look like this:
mvn -e install:install-file -Dfile=C:\Oracle\Middleware\wlserver_10.3\server\lib\wlthint3client.jar -DgroupId=weblogic -DartifactId=weblogic -Dversion=10.3 -Dpackaging=jar
Once the libraries are added to the local Maven repository as shown above, then the ‘pom.xml’ file for the given project can include the dependencies, using the groupId, artifactId, and version in order to identify the dependency to Maven. Maven should then be able to find the dependency in the local repo and use it when running the unit test.
    <dependency>
            <groupId>weblogic</groupId>
            <artifactId>weblogic</artifactId>
            <version>10.3</version>
    </dependency>
Note: We do not instantiate weblogic JMS objects in code, rather they are injected via Spring XML. So there is no compile-time requirement for the Weblogic JAR. The requirement for this dependency to be specified comes only when the unit test is run. We’ll talk more about how to make this JAR available when running the bundle inside Servicemix in the next section.
Building the example
First make sure you have built the example and installed into local maven repo:
mvn clean install
This will build the bundle (place the resulting jar into the /target folder), install the bundle into the local Maven respository, and run the JUnit test.
If the build and unit test succeeds you are ready to install the bundle into Servicemix.
As an alternative to running this route in Servicemix, you can also run the camel route in standalone mode (thanks to the camel-maven-plugin in the pom) by typing
mvn camel:run
You can see the routing rules by looking at the java code in the src/main/java directory and the Spring XML configuration lives in src/main/resources/META-INF/spring
Making WebLogic JMS client library Available inside Servicemix (Runtime)

The weblogic client library needs to be installed into FUSE ESB (Servicemix) making it available at run time. Assuming you have run the 'mvn install:install-file' command to install the weblogic library into the local maven repository, you should now be able to install the library into Servicemix by pointing Servicemix to the proper location in the local maven repo (using the'wrap:' and 'mvn:' prefix.
From the Servicemix command-line console type:
osgi:install wrap:mvn:weblogic/weblogic/10.3\$Bundle-SymbolicName=weblogic&Bundle-Version=10.3
Once the dependent runtime Weblogic client JAR file is installed in Servicemix, you can install the WebLogic example route into Servicemix.
Installing the Route
You have 3 options for installing the bundle. Choose one of the following techniques.
1) Install as feature
The pom file does the necessary work to create a feature file representing the bundle. This feature file is installed alongside the bundle into the local maven repository. You can add the feature descriptor to the set of feature descriptors that Servicemix knows about by issuing the following command:
features:addurl  mvn:com.fusesource.examples/camel-weblogic-osgi/1.0.0/xml/features
You can then install the "camel-weblogic-osgi" feature by running the following:
features:install  camel-weblogic-osgi
2) Install as a bundle
Before doing this make sure the dependent runtime features have been installed:
features:install camel-jms
features:install camel-jetty
osgi:install wrap:mvn:weblogic/weblogic/10.3\$Bundle-SymbolicName=weblogic&Bundle-Version=10.3
Once above dependencies are successfully installed into Servicemix, you can install the example route into servicemix using the following from the servicemix command console:
osgi:install -s mvn:com.fusesource.examples/camel-weblogic-osgi/1.0.0
3) Copy JAR into hotdeploy directory
Before doing this make sure the dependent features specified in option 2 above “Install as a bundle” have been installed. Once these dependencies are successfully installed into Servicemix, you can install the example route while Servicemix is running, drop the file target\camel-weblogic-osgi-1.0.0.jar into the \deploy directory of your Servicemix installation.
Logging and Troubleshooting
Make sure there is no other route listening for request on port 8888.
To enable logging for this example type:
log:set DEBUG  com.fusesource.example.camel
With logging set to DEBUG for 'com.fusesource.example.camel', search the log for "Message body =" (see the debug statements in MyTransform.java).
For an even higher level of logging (logging Camel specifics)
log:set DEBUG org.apache.camel
See logs in <servicemix_home>\data\logs\servicemix.log
Testing the Route
To trigger the route you can perform an HTTP POST at the URL "http://localhost:8888/placeorder", sending in any valid XML as the content of the message. SOAPUI is a good (free) tool for sending test HTTP requests.
The route will take the content of the incoming HTTP post and put it on the appropriate queue (as specified in the "camel-context.xml".)
Typically, the process that is listening on this queue is external to the route. It is expected that the process will pick up the message and place a response on the designated response queue (copying messageId into correlationId) per JMS request/reply convention. If this pattern is followed by the external process, then Camel will be able to receive the response on expected queue and correlate it with the request.