Skip to main content

JMS Over HTTP using OpenMQ (Sun Java System Message Queue and HTTP tunneling)

Posted by kalali on June 19, 2009 at 1:31 PM PDT

You may have already faced situation when you need to have messaging capabilities in your application while your client application runs in an environment which you have no control over its network configuration and restrictions. Such situation lead to the fact that you can not use JMS communication over designated ports like 7676 and so.

You may simply put JMS away and follow another protocol or even plain HTTP communication to address your architecture and design requirement, but we can not easily put pure JMS API capabilities away as we may need durable subscription over a proven and already well tested and established design and architecture. Different JMS implementation providers provide different type capabilities to address such a requirement. ActiveMQ provide HTTP and HTTPS transport for JMS API and described it here.

OpenMQ provide different transport protocol and access channels to access OpenMQ functionalities from different prgramming . One of the access channles which involve HTTP is named Universal Message Service (UMS) which provide a simple REST interaction template to place messages and comsume them from any programming language and device which can interact with a network server. UMS has some limitations which is simply understandable based on the RESTful nature of UMS. For current version of OpenMQ, it only supports Message Queues as destinations so there is no publish-subscribe functionality available.

Another capability which involve HTTP is JMS over HTTP or simply JMS HTTP tunneling. OpenMQ JMS implementation supports HTTP/s as transport protocol if we configure the message broker properly. Well I said the JMS implementation support HTTP/S as a transport protocol which means we as user of the JMS API see almost no difference in the way that we use the JMS API. We can use publish-subscribe, point to point, durable subscription, transaction and whatever provided in the JMS API.

First, lets overview the OpenMQ installation and configuration then we will take a look at an example to see what changes between using JMS API and JMS API over HTTP transport. Installation process is as follow:

    OpenMQ project provides a Java EE web application which interact with OpenMQ broker from one side and Sun JMS implementation on the other side. Interaction of this application with the client and MQ broker is highly customizable in different aspects like Broker port number, client poll inrval, Broker address and so on.

  • Download OpenMQ from https://mq.dev.java.net/ it should be a zip which is different for each platform.
  • Install the MQ by unzipping the above file and running ./installer or installer.bat or so.
  • After the installation completed, go to install_folder/var/mq/instances/imqbroker/props and open the config.properties in a text editor like notepad or emeditor or gedit.
  • Add the following line to the end of the above file:
  •  imq.service.activelist=jms,admin,httpjms 
  • Now goto install_folder/mq/lib/ and pick the imqhttp.war file. deploy the file into your Servlet container or application server (I went with GlassFish). After you deployed the file start the application server or Servlet container
  • Now it is time to start the MQ broker: launch a terminal or cmd console and goto install_folder/mq/bin now execute ./imqbrokerd -port 7979 (it maybe like imqbrokerd.bat -port 7979 for Windows ) This command will start the MQ broker and keep it listening on port 7979 for incoming connection
  • To test the overall operations: Open a browser and tray to surf http://127.0.0.1:8080/imqhttp/tunnel or whatever URL which points to the newly deployed application . If you saw "HTTP tunneling Servlet ready." as the first line in the response page then we are ready for last step.

Now let's see how we can publish some messages, this sample code assume that we have configured the message broker and assumes that we have the following two JAR files in the classpath. These JAR files are available in install_folder/mq/lib/

 

  1. imq.jar
  2. jms.jar

Now the Publisher code:

 public class Publisher {      public void publish(String messageContent) {         try {             String addressList = "http://127.0.0.1:8080/imqhttp/tunnel";             com.sun.messaging.TopicConnectionFactory topicConnectionFactory = new com.sun.messaging.TopicConnectionFactory();             topicConnectionFactory.setProperty(com.sun.messaging.ConnectionConfiguration.imqAddressList, addressList);             javax.jms.Topic top;             javax.jms.Connection con = topicConnectionFactory.createTopicConnection("admin", "admin");              javax.jms.Session session = con.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);             top = session.createTopic("DIRECT_TOPIC");             MessageProducer prod = session.createProducer(top);             Message textMessage = session.createTextMessage(messageContent);             prod.send(textMessage);          prod.close();         session.close();         con.close();                  } catch (JMSException ex) {             ex.printStackTrace();         }      }      public static void main(String args[]) {          Publisher p = new Publisher();         for (int i = 1; i < 10; i++) {             p.publish("Sample Text Message Content: " + i);         }     } } 

As you can see the only difference is the connection URL which uses http instead of mq and point to a Servlet container address instead of pointing to the Broker listening address.

The subscriber sample code follow a similar pattern. Here I write a sample durable subscriber so you can see that we can use durable subscribtion over HTTP. But you should note that HTTP transport uses polling and continuesly open communication channel which can introduce some overload on the server.

 class SimpleListener implements MessageListener {      public void onMessage(Message msg) {         System.out.println("On Message Called");         if (msg instanceof TextMessage) {             try {                 System.out.print(((TextMessage) msg).getText());             } catch (JMSException ex) {                 ex.printStackTrace();             }          }     } }  public class Subscriber {      /**      * @param args the command line arguments      */     public void subscribe(String clientID, String susbscritpionID) {         try {             // TODO code application logic here              String addressList = "http://127.0.0.1:8080/imqhttp/tunnel";             com.sun.messaging.TopicConnectionFactory topicConnectionFactory = new com.sun.messaging.TopicConnectionFactory();             topicConnectionFactory.setProperty(com.sun.messaging.ConnectionConfiguration.imqAddressList, addressList);             javax.jms.Topic top;             javax.jms.Connection con = topicConnectionFactory.createTopicConnection("admin", "admin");             con.setClientID(clientID);             javax.jms.Session session = con.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);             top = session.createTopic("DIRECT_TOPIC");             TopicSubscriber topicSubscriber = session.createDurableSubscriber(top, susbscritpionID);             topicSubscriber.setMessageListener(new SimpleListener());              con.start();           } catch (JMSException ex) {             ex.printStackTrace();         }     }      public static void main(String args[]) {          Subscriber sub = new Subscriber();         sub.subscribe("C19", "C1_011");     } } 

Now how you can test the entire example and monitor the MQ? it is very simple by utilizing the provided tools.

Do the following steps to test the overall durable subscription system:

  • Run a subscriber
  • Run another subscriber with a different client ID
  • Run a publisher once or twice
  • Kill the second subscriber
  • Run a publisher once
  • Run the subscriber and you can see that the subscriber will fetch all messages which arrived after it shut down-ed.

Note that we can not have two separate client with same client ID running because the broker will not be able to distinguish which client it should send the messages.

  • You can monitor the queue and the broker using: ./imqadmin which can be found in install_folder/mq/bin this software shows how many durable subscribers are around and how many messages are pending for each subscriber and so on.
  • You can monitor the Queue in real-time mode using the following command which can be executed in install_folder/mq/bin

./imqcmd -b 127.0.0.1:7979 metrics dst -t t -n DIRECT_TOPIC The command will ask for username and password, give admin/admin for it

The sample code for this entry can be found Here. The sample code is a NetBeans project with the publisher and the subscriber source code.

A complete documentation of OpenMQ is available at its documentation centre. You can see how you can change different port numbers or configure different properties of the Broker and HTTP tunneling web application communication.

 


Related Topics >>

Comments

Using the JMS over HTTP with the EMBEDDED broker

Hi, I wonder whether this works out-of-the-box with the EMBEDDED MQ broker of the Glassfish. In that case, the explicit step to start the broker is not needed. Any special configuration needed for this? Thanks, Jörg

HTTP for BLOBs

Hi, Good post, could have done with that a year ago :) I have been using JMS over HTTP with OpenMQ for about a year now, and for a TextMessage it is pretty good. However, a StreamMessage with a BLOB is a noticeable difference, it is at least 3 times slower than TCP to send the message, end to end for about 50MB is taking upwards of 4 minutes. I can only assume this is because each message is now performing a triple hop as it has to go through the servlet? I would be interested to know what speed you can send ~50MB at from end-to-end. Also, I have been trying to work out how the servlet sends the message on to the broker, when it has no obvious knowledge of the broker other than a port. I had originally assumed that the servlet would merely push the message onwards to the broker via TCP, however, I now wonder if it is the broker who connects to the servlet and pulls? Many thanks Ror

I have it working now. Thanks for the help and the post.

@dgavenda, There are two possibilities that you can not see the instance folder: first: You should start the broker at leas one time to have that instance folder created. You may had not started the server before you have looked for the file. second which I can hardly imagine about your case is a configuration element named IMQ_DEFAULT_VARHOME. this element specifies the default var home of you openMQ installation. You can check its value by looking at install_folder/etc/imqenv.conf file. hope it helps

It seems this refers to OpenMQ 4.1. I was able to find that file in 4.1. Where is the similar config file in 4.3? Or would you suggest to use 4.1?

"After the installation completed, go to install_folder/var/mq/instances/imqbroker/props and open the config.properties in a text editor like notepad or emeditor or gedit. " I am using the binary version of openMQ 4.3 for Linux. After I unzip the zip file and run ./installer, I do not have anything in the install_folder/var/mq/instances directory. Also, there doesn't seem to be a config.properties file in the MessageQueue directory. I checked recursively too. What am I missing?

Fantastic, thank you.

An untrusted applet can communicate back to a server which it is initiated from (server which hosted the applet and delivered the .class and jar files). So your applet can either use mq protocol or it can use JMS over HTTP protocol which is shown in the sample.

Is this technique compatible with the applet security rules? Can I have an unsigned applet that connects back to the server it was loaded from using JMS over HTTP? That would be really cool.