Creating Secure JMS Connections with Apache ActiveMQ

Archived Content
This article is provided for historical perspective only, and may not reflect current conditions. Please refer to relevant product page for more up-to-date product information and resources.
  • By Senaka Fernando
  • 13 Dec, 2009

Introduction

Reliability becomes a crucial requirement for most JMS connections that enable asynchronous exchange of critical business messages. To facilitate this requirement, most modern JMS Brokers provide the facility to establish secure connections with clients. The WSO2 Carbon Framework supports establishing secure JMS connections with brokers such as Apache ActiveMQ.

The Java Message Service (JMS) defines the standard for reliable Enterprise Messaging. Enterprise messaging, often also referred to as Messaging Oriented Middleware (MOM), is universally recognized as an essential tool for building enterprise applications, which provides a reliable, flexible service for the asynchronous exchange of critical business data and events throughout an enterprise. A typical JMS implementation adds to this a common API and provider framework that enables the development of portable, message based applications in the Java programming language. By combining Java technology with enterprise messaging, the JMS API provides a powerful tool for solving enterprise computing problems, [1].

Reliability therefore becomes a crucial requirement for most JMS connections that enable asynchronous exchange of critical business messages. To facilitate this requirement, most modern JMS Brokers provide the facility to establish secure connections with clients. The WSO2 Carbon Framework supports establishing secure JMS connections with brokers such as Apache ActiveMQ, [2].

This brief tutorial outlines the basic steps that need to be followed to configure a WSO2 Carbon Framework based product and establish a secure JMS connection with Apache ActiveMQ. In here, we use the WSO2 Enterprise Service Bus (ESB) as an example. The same procedure can be followed to configure any other product based on the WSO2 Carbon Framework.

 

Configuring the JMS Transport

Step 1: Download the latest version of the WSO2 ESB from here. Extract the downloaded zip in to a directory in your file system. We will call this ESB_HOME.

Step 2: Download Apache ActiveMQ 5.3.0 from here. Extract the downloaded zip in to a directory in your file system. We will call this ACTIVEMQ_HOME.

Before setting up a secure JMS connection, we have to create compatible Java Key and Trust stores so that we can share a certificate which is capable of proving our authenticity. The WSO2 ESB contains pre-configured Key and Trust stores that can be used for this purpose. However, if you need to create your own, you can follow the instructions mentioned in here.

In this tutorial we will be using the pre-configured Key and Trust stores of the WSO2 ESB which can be found in ESB_HOME/resources/security/.

If you want to create new Key and Trust stores for the message broker, please note that you will have to import the corresponding certificate files to the pre-configured Key and Trust stores of the WSO2 ESB. This might be required if the the message broker and the WSO2 Carbon Framework based product are installed on two physically separate systems.

Step 3: Configure the Apache ActiveMQ broker according to the instructions found in here. To configure the broker, you can use one of the two methods mentioned.

In this tutorial we will be using Spring to configure SSL for the Broker instance. This can be done by adding the following XML configuration. (Please note the ESB_HOME used for this example is /home/user/wso2esb-2.1.2.)

   <sslContext>
     <sslContext
         keyStore="file:///home/user/wso2esb-2.1.2/resources/security/wso2carbon.jks" keyStorePassword="wso2carbon"
         trustStore="file:///home/user/wso2esb-2.1.2/resources/security/client-truststore.jks" trustStorePassword="wso2carbon"/>
   </sslContext>

Step 4: Start the Apache ActiveMQ broker. This can be done by running ./bin/activemq within the ACTIVEMQ_HOME. More information on starting ActiveMQ can be found in here. If you have successfully configured the broker, you should see something similar to the following lines being printed on your console.

 INFO | Listening for connections at: ssl://user-laptop:61616
 INFO | Connector openwire Started
 INFO | ActiveMQ JMS Message Broker (localhost, ID:user-laptop-53930-1260687396155-0:0) started

Step 5: In order to use the JMS Transport of the WSO2 ESB with Apache ActiveMQ, you need to replace the following lines found in ESB_HOME/conf/axis2.xml,

    <!--Uncomment this and configure as appropriate for JMS transport support, after setting up your JMS environment (e.g. ActiveMQ)
    <transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">
        <parameter name="myTopicConnectionFactory" locked="false">
                <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
                <parameter name="java.naming.provider.url" locked="false">tcp://localhost:61616</parameter>
                <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">TopicConnectionFactory</parameter>
                <parameter name="transport.jms.ConnectionFactoryType" locked="false">topic</parameter>
        </parameter>

        <parameter name="myQueueConnectionFactory" locked="false">
                <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
                <parameter name="java.naming.provider.url" locked="false">tcp://localhost:61616</parameter>
                <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>
                <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
        </parameter>

        <parameter name="default" locked="false">
                <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
                <parameter name="java.naming.provider.url" locked="false">tcp://localhost:61616</parameter>
                <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>
                <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
        </parameter>
    </transportReceiver>-->

with the lines,

    <!--Uncomment this and configure as appropriate for JMS transport support, after setting up your JMS environment (e.g. ActiveMQ) -->
    <transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">
        <parameter name="myTopicConnectionFactory" locked="false">
                <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
                <parameter name="java.naming.provider.url" locked="false">ssl://localhost:61616</parameter>
                <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">TopicConnectionFactory</parameter>
                <parameter name="transport.jms.ConnectionFactoryType" locked="false">topic</parameter>
        </parameter>

        <parameter name="myQueueConnectionFactory" locked="false">
                <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
                <parameter name="java.naming.provider.url" locked="false">ssl://localhost:61616</parameter>
                <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>
                <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
        </parameter>

        <parameter name="default" locked="false">
                <parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
                <parameter name="java.naming.provider.url" locked="false">ssl://localhost:61616</parameter>
                <parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>
                <parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
        </parameter>
    </transportReceiver>

This will enable the transport receiver. To enable the transport sender, you should also replace the following lines,

     <!-- uncomment this and configure to use connection pools for sending messages>
     <transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender"/-->

with the lines,

     <!-- uncomment this and configure to use connection pools for sending messages> -->
     <transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender"/>

Step 6: Add the dependencies required by the WSO2 ESB to connect to Apache ActiveMQ. You will need to copy the ActiveMQ client jar files activeio-core-3.1.2.jar, activemq-core-5.3.0.jar, geronimo-jms_1.1_spec-1.1.1.jar and geronimo-j2ee-management_1.0_spec-1.0.jar into the repository/components/lib directory to allow ESB to connect to the JMS provider.

Step 7: Start the WSO2 ESB. This can be done by running ./wso2server.sh within the ESB_HOME/bin directory. More information on starting the WSO2 ESB can be found in here. If you have successfully configured the WSO2 ESB, you should see something similar to the following lines being printed on your console.

[2009-12-13 12:42:29,110]  INFO - JMSSender JMS Sender started
[2009-12-13 12:42:29,111]  INFO - JMSSender JMS Transport Sender initialized...
...
[2009-12-13 12:42:34,713]  INFO - JMSConnectionFactory JMS ConnectionFactory : myTopicConnectionFactory initialized
[2009-12-13 12:42:34,716]  INFO - JMSConnectionFactory JMS ConnectionFactory : myQueueConnectionFactory initialized
[2009-12-13 12:42:34,718]  INFO - JMSConnectionFactory JMS ConnectionFactory : default initialized
[2009-12-13 12:42:34,719]  INFO - JMSListener JMS Transport Receiver/Listener initialized...

Now you should be able to communicate via JMS over a secure channel.

 

Trying out Samples

Having done the configuration and established a successful JMS connection with the Apache ActiveMQ broker, you might be interested in trying out some of the samples found in the WSO2 ESB. In this tutorial, we will be looking at the sample 250 explained in here. The WSO2 ESB samples are designed to run over a non-secure JMS connection and therefore, by simply following the steps mentioned, you should end up seeing the following exception being printed on your console.

jmsclient:
     [java] Exception in thread "main" javax.jms.JMSException: java.io.EOFException
     [java] 	at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:62)
     [java] 	at org.apache.activemq.ActiveMQConnection.syncSendPacket(ActiveMQConnection.java:1266)
     [java] 	at org.apache.activemq.ActiveMQConnection.ensureConnectionInfoSent(ActiveMQConnection.java:1350)
     [java] 	at org.apache.activemq.ActiveMQConnection.createSession(ActiveMQConnection.java:300)
     [java] 	at org.apache.activemq.ActiveMQConnection.createQueueSession(ActiveMQConnection.java:1192)
     [java] 	at samples.userguide.GenericJMSClient.connect(Unknown Source)
     [java] 	at samples.userguide.GenericJMSClient.main(Unknown Source)
     [java] Caused by: java.io.EOFException
     [java] 	at java.io.DataInputStream.readInt(DataInputStream.java:358)
     [java] 	at org.apache.activemq.openwire.v5.BaseCommandMarshaller.looseUnmarshal(BaseCommandMarshaller.java:100)
     [java] 	at org.apache.activemq.openwire.v5.BrokerInfoMarshaller.looseUnmarshal(BrokerInfoMarshaller.java:154)
     [java] 	at org.apache.activemq.openwire.OpenWireFormat.doUnmarshal(OpenWireFormat.java:368)
     [java] 	at org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:279)
     [java] 	at org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:210)
     [java] 	at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:202)
     [java] 	at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:185)
     [java] 	at java.lang.Thread.run(Thread.java:595)
     [java] Java Result: 1

In order to run this client in a secure environment you will need to change the java.naming.provider.url in the GenericJMSClient (found in ESB_HOME/samples/axis2Client/src/samples/userguide/GenericJMSClient.java) to ssl://localhost:61616. You will then need to set the following system properties.

javax.net.ssl.trustStore=/path/to/client.ts
javax.net.ssl.trustStorePassword=password

This can be done by replacing the following lines found in ESB_HOME/samples/axis2Client/build.xml

    <target name="jmsclient" depends="compile">
        <java classname="samples.userguide.GenericJMSClient"
              classpathref="javac.classpath" fork="true">
            <sysproperty key="jms_dest"    value="${jms_dest}"/>
            <sysproperty key="jms_type"    value="${jms_type}"/>
            <sysproperty key="jms_payload" value="${jms_payload}"/>
            <sysproperty key="java.io.tmpdir" value="./../../tmp/sampleClient"/>
        </java>
    </target>

with the lines,

    <target name="jmsclient" depends="compile">
        <java classname="samples.userguide.GenericJMSClient"
              classpathref="javac.classpath" fork="true">
            <sysproperty key="jms_dest"    value="${jms_dest}"/>
            <sysproperty key="jms_type"    value="${jms_type}"/>
            <sysproperty key="jms_payload" value="${jms_payload}"/>
            <sysproperty key="java.io.tmpdir" value="./../../tmp/sampleClient"/>
            <sysproperty key="javax.net.ssl.trustStore" value="./../../resources/security/client-truststore.jks"/>
            <sysproperty key="javax.net.ssl.trustStorePassword" value="wso2carbon"/>
        </java>
    </target>

You then should see the sample being successfully executed, along with a confirmation as follows.

jmsclient:

BUILD SUCCESSFUL
Total time: 2 seconds

Similarly, you should be able to run the other samples that are shipped with the WSO2 ESB with after the respective modifications have been done.

 

References

[1] Java Message Service API Overview
[2] Apache ActiveMQ -- SSL Transport Reference
[3] Running the Transport samples with WSO2 Enterprise Service Bus (ESB)

 

Author

Senaka Fernando, Software Engineer, WSO2, [email protected]