JMS Bridge Example

This example shows you how to create a JMS Bridge between two ActiveMQ servers.

The example will use two ActiveMQ servers:

Both ActiveMQ server will run their own JNDI server used by the JMS Bridge and the JMS Client to lookup JMS resources (ConnectionFactory and Destination).

The JMS Bridge will be started in the example code and be configured to bridge messages from the source destination (the topic hosted on server #0) and the target destination (the queue hosted on server #1)

The client will check the bridge works by:

  1. sending a message to the source topic
  2. receive a message from the target queue
  3. check that both messages correspond to the same content.

JMS Bridge Configuration

The JMS Bridge is a POJO that we configure with both source and target JNDI configurations. In the actual example we are programatically creating the Bridge, however the following section describes how you would do this if you wanted to deploy with an actual ActiveMQ server via the activemq-beans.xml.

Configuring the Bridge with the JBoss Microcontainer

in which we inject JNDI configurations so that it looks up its source and target JMS resources. The JMS Bridge is defined a bean and setup by JBoss Microntainer in the same VM than Server #1, the target server.

The JMS Bridge sample configuration can be found in resources/activemq-beans.xml, firstly we define the Bridge itself:

         <!-- The JMS Bridge -->
         <bean name="JMSBridge" class="org.apache.activemq.jms.bridge.impl.JMSBridgeImpl">
         ...
         </bean>
      

the JMSBridgeImpl constructor is used to inject all the properties required to run the JMS Bridge.

Its first four arguments defines how the bridge will lookup:

  1. its source JMS ConnectionFactory
  2. its source JMS Destination
  3. its target JMS ConnectionFactory
  4. its target JMS Destination

Using other POJOs, the JMS Bridge is configured to retrieve:

In turn, SourceJNDI and TargetJNDI are POJOs defining how to connect to JNDI server. SourceJNDI URL must point to your source server, while LocalJNDI must point to your target server:

      <bean name="SourceJNDI" class="java.util.Hashtable">
         ...
                <entry>
                   <key>java.naming.provider.url</key>
                   <!-- **************************************** -->
                   <!-- Replace with the *source* server address -->
                   <!-- **************************************** -->
                   <value>jnp://192.168.0.10:1099</value>
         ...
       </bean>
       <bean name="TargetJNDI" class="java.util.Hashtable">
         ...
                <ntry>
                   <key>java.naming.provider.url</key>
                   <!-- **************************************** -->
                   <!-- Replace with the *target* server address -->
                   <!-- **************************************** -->
                   <value>jnp://1192.168.0.11:1099</value>
                </entry>
         ...
       </bean>
      

Example step-by-step

To run the example after having setup both ActiveMQ servers and the JMS bridge:

  1. To run the example simply run mvn verify -Pexample

Let's look at the Client code (in JMSBridgeExample class):

  1. First we need to get an initial context so we can look-up the JMS resources
  2.            InitialContext sourceContext = createContext(sourceServer);
               InitialContext targetContext = createContext(targetServer);
            
  3. We then create a JMS Bridge and start it, Note, for certain quality of service modes such as ONCE_AND_ONCE_ONLY and AT_LEAST_ONCE a Transaction Manager is required to ensure Messages are delivered accordingly. A Transaction Manager can be either loaded via implementation of TransactionManagerLocator intefer and loaded via standard a ServiceLoader or by explicitly setting an instance of a Transaction Manager on the bridge using setTranscationManager(TransactionManager tm) method. In this example we'll be using the DUPLICATES_OK quality of service so there is no need for a Transaction Manager.
                JMSBridge jmsBridge = new JMSBridgeImpl(
                   new JNDIConnectionFactoryFactory(sourceJndiParams, "source/ConnectionFactory"),
                   new JNDIConnectionFactoryFactory(targetJndiParams, "target/ConnectionFactory"),
                   new JNDIDestinationFactory(sourceJndiParams, "source/topic"),
                   new JNDIDestinationFactory(targetJndiParams, "target/queue"),
                   null,
                   null,
                   null,
                   null,
                   null,
                   5000,
                   10,
                   QualityOfServiceMode.DUPLICATES_OK,
                   1,
                   -1,
                   null,
                   null,
                   true);
                ....
            jmsBridge.start();
            
  4. We look up the JMS resources from the Source server
  5.            ConnectionFactory sourceConnectionFactory = (ConnectionFactory)sourceContext.lookup("source/ConnectionFactory");
               Topic sourceTopic = (Topic)sourceContext.lookup("source/topic");
            
  6. We create JMS objects to send a message to the source destination
  7.            sourceConnection = sourceConnectionFactory.createConnection();
               Session sourceSession = sourceConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
               MessageProducer sourceProducer = sourceSession.createProducer(sourceTopic);
            
  8. We send a message to the source destination
  9.            TextMessage message = sourceSession.createTextMessage("this is a text message sent at " + System.currentTimeMillis());
               sourceProducer.send(message);
            
  10. We close the connection to the source server
  11.            sourceConnection.close();
            

    At this point, the JMS Bridge will consume the message from the source topic and sends it to the target queue. The client will check the bridge works by consuming a message from the target queue.

  12. We look up the JMS resources from the target server
  13.            ConnectionFactory targetConnectionFactory = (ConnectionFactory)targetContext.lookup("target/ConnectionFactory");
               Queue targetQueue = (Queue)targetContext.lookup("target/queue");
            
  14. We create JMS objects to receive a message from the target destination
  15.            targetConnection = targetConnectionFactory.createConnection();
               Session targetSession = targetConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
               MessageConsumer targetConsumer = targetSession.createConsumer(targetQueue);
           
  16. We start the target connection to start receiving messages
               targetConnection.start();
            
  17. We receive the message and print it. Its content is the same than the message the client sent to the source topic
  18.            TextMessage messageReceived = (TextMessage)targetConsumer.receive(5000);
            
  19. We display the message ID and its "bridged" message ID
  20.            System.out.format("Message ID         : %s\n", messageReceived.getJMSMessageID());
               System.out.format("Bridged Message ID : %s\n", messageReceived.getStringProperty("AMQ_BRIDGE_MSG_ID_LIST"));
            

    Note that the message received from the target queue is not the same message sent to the source topic (their message IDs are different) but they have the same content.

  21. And finally, we stop the Bridge and always remember to close your JMS connections and resources after use, in a finally block. Closing a JMS connection will automatically close all of its sessions, consumers, producer and browser objects
  22.            finally
               {
                  if (jmsBridge != null)
                  {
                    jmsBridge.stop();
                  }
                  if (initialContext != null)
                  {
                    initialContext.close();
                  }
                  if (connection != null)
                  {
                     connection.close();
                  }
               }
            

More information