Saturday, June 18. 2011
Usign OpenMQ with Tomcat
Some weeks ago a company mate asked me some questions about a JavaEE application that needs a JMS queue for a special process. The customer wanted to install the software inside Tomcat and I warned her that Tomcat is just a Servlet container and not a fully JavaEE application server, therefore she needed another JMS server software. Although I have never worked with open source JMS servers I advised to use ActiveMQ but the problem was the customer already used OpenMQ. I tried to find some examples about this configuration but it was impossible, so I finally could not help very much. But yesterday I had some free time and I tried it by myself.
OpenMQ is the Oracle (formerly Sun) open source JMS server used in Glassfish. It used to be tightly coupled with the application server but the last versions are trying to split the two platforms. Nevertheless I was unable to find any example of how to integrate OpenMQ inside Tomcat, so this shows the division is just underway. I decided to follow a very good guide for setting ActiveMQ in Tomcat and then perform the needed changes.
OpenMQ can be downloaded from its web page and there is a simple ZIP distribution file. Just uncompressing the file a MessageQueue4_5 directory is popped out with all the necessary bits. From here this directory will be called {OPENMQ_DIR}.
$ unzip openmq4_5-binary-Linux_X86.zip
After installation the broker (the JMS java daemon) can be just started.
{OPENMQ_DIR}/mq/bin$ ./imqbrokerd & [17/Jun/2011:17:14:46 CEST] ============================================== Open Message Queue 4.5 Oracle Version: 4.5 (Build 29-b) Compile: Wed Feb 9 22:53:30 PST 2011 Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. =============================================== Java Runtime: 1.6.0_26 Sun Microsystems Inc. /usr/lib/jvm/java-6-sun-1.6.0.26/jre [17/Jun/2011:17:14:46 CEST] IMQ_HOME=/home/blog/MessageQueue4_5/mq [17/Jun/2011:17:14:46 CEST] IMQ_VARHOME=/home/blog/MessageQueue4_5/var/mq [17/Jun/2011:17:14:46 CEST] Linux 2.6.38-2-amd64 amd64 magneto (4 cpu) ricky [17/Jun/2011:17:14:46 CEST] Java Heap Size: max=188416k, current=188416k [17/Jun/2011:17:14:46 CEST] Arguments: [17/Jun/2011:17:14:46 CEST] [B1060]: Loading persistent data... [17/Jun/2011:17:14:46 CEST] Using built-in file-based persistent store: /home/blog/MessageQueue4_5/var/mq/instances/imqbroker/ [17/Jun/2011:17:14:46 CEST] WARNING [B3168]: Invalid broker address for this broker to run in cluster: Loopback IP address is not allowed in broker address mq://127.0.1.1:7676/?instName=imqbroker&brokerSessionUID=6784163344567163648 for cluster [17/Jun/2011:17:14:46 CEST] WARNING [B1137]: Cluster initialization failed. Disabling the cluster service. [17/Jun/2011:17:14:46 CEST] [B1270]: Processing messages from transaction log file... [17/Jun/2011:17:14:46 CEST] [B1039]: Broker "imqbroker@magneto:7676" ready.
Of course the software can be configured in more detail (OpenMQ supports high availability, different backends to store messages and so on) but it was not the goal of this entry. With this nonexistent configuration the broker uses the directory {OPENMQ_DIR}/instances/imqbroker, inside it there are several things: the configuration properties file, the directory where messages are stored by default,...
Once the broker is up and ready a queue called sampleQueue is created.
{OPENMQ_DIR}/mq/bin$ ./imqcmd create dst -n sampleQueue -t q -u admin Password: Creating a destination with the following attributes: Destination Name sampleQueue Destination Type Queue On the broker specified by: ------------------------- Host Primary Port ------------------------- localhost 7676 Successfully created the destination.
OpenMQ admin user also has admin as the default password (although it can be easily changed using imqusermgr update -u admin -p newpassword). With an OpenMQ queue ready to be used the next step is configuring Tomcat resources. But, obviously, we need first to include client libraries in Tomcat classpath. I just copied the jars in the lib directory. At this point I am assuming a Tomcat 6 server is installed in {TOMCAT_DIR} directory.
$ cp {OPENMQ_DIR}/mq/lib/imq.jar {TOMCAT_DIR}/lib $ cp {OPENMQ_DIR}/mq/lib/jms.jar {TOMCAT_DIR}/lib
And now the hard part comes, cos now I need to define the resources in Tomcat to be populated in the JNDI tree, and this was the part I could not find documentation anywhere. Looking OpenMQ source code (that's the way I wanna Open Source) I discover that OpenMQ has some JNDI ObjectFactory classes (an ObjectFactory is the way Tomcat creates and loads any kind of object in the JNDI tree). All of them are placed in the com.sun.messaging.naming package. After some tries (I will never admit how many) I discovered the factory and the queue have to be defined in the GlobalNamingResources of the server.xml file this way.
<GlobalNamingResources> ... <Resource name="jms/sampleFactory" auth="Container" type="com.sun.messaging.QueueConnectionFactory" description="OpenMQ Queue Connection Factory" factory="com.sun.messaging.naming.QCFObjectFactory" securityPort="7676" parm="--" subnet="0" host="-s localhost" ackTimeout="-t 30000" version="1.1" /> <Resource name="jms/sampleQueue" auth="Container" type="com.sun.messaging.Queue" description="OpenMQ Queue" factory="com.sun.messaging.naming.QObjectFactory" destName="sampleQueue" version="1.1" /> </GlobalNamingResources>
I really do not know why the references are so exotic but, as I said before, it seems the reason is the high coupling with former Sun application server (if you see some values are like arguments to start the broker daemon). With this part solved the rest of the configuration is just like ActiveMQ or any other JMS software. We need to add the references in the default context (context.xml file).
<Context> ... <ResourceLink global="jms/sampleFactory" name="jms/sampleFactory" type="javax.jms.ConnectionFactory"/> <ResourceLink global="jms/sampleQueue" name="jms/sampleQueue" type="javax.jms.Queue"/> </Context>
And finally a little web application is necessary to test the setup. I quickly developed a servlet with a send and receive (consume) method, the complete Netbeans project can be downloaded here. In short the web.xml file has to define again the references.
<resource-ref> <res-ref-name>jms/sampleFactory</res-ref-name> <res-type>javax.jms.ConnectionFactory</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> <resource-ref> <res-ref-name>jms/sampleQueue</res-ref-name> <res-type>javax.jms.Queue</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref>
And here it is the doSend method (nothing special was done).
private void doSend(String txt) throws ServletException { QueueConnection connection = null; QueueSession queueSession = null; try { System.out.println("Getting connection factory from JNDI..."); QueueConnectionFactory connFactoryObj = (QueueConnectionFactory) initialContext.lookup("java:/comp/env/jms/sampleFactory"); System.out.println("Creating connection..."); connection = connFactoryObj.createQueueConnection("admin", "admin"); queueSession = connection.createQueueSession(true, Session.SESSION_TRANSACTED); System.out.println("Getting queue from JNDI..."); Queue queue = (Queue) initialContext.lookup("java:/comp/env/jms/sampleQueue"); System.out.println("Creating the session"); QueueSender queueSender = queueSession.createSender(queue); TextMessage message = queueSession.createTextMessage(); message.setText(txt); System.out.println("Sending the message: " + message); queueSender.send(message); queueSession.commit(); } catch (Exception e) { if (queueSession != null) { try {queueSession.rollback();} catch(Exception ex) {} } e.printStackTrace(); throw new ServletException(e); } finally { if (queueSession != null) { try {queueSession.close();} catch(Exception e) {} } if (connection != null) { try {connection.close();} catch(Exception e) {} } } }
Finally I decided to extend or improve this solution. In the resource configuration only host and port are specified, this means a lot of the JMS software features are wasted. OpenMQ right now does not recommend the use of imqBrokerHostName or imqBrokerHostPort properties, now it uses a new imqAddressList which defines one or more broker instances (HA setup). So I created a new ObjectFactory called CFReflectionObjectFactory.java which simply lists all the connection configuration properties of OpenMQ (using reflection over the class ConnectionConfiguration) and set the property if it is defined in the Tomcat resource definition. This way any property can be passed to OpenMQ connection. Using this little improvement (I also copied the one class jar library inside the Tomcat lib directory) the Resource for the factory was as follows.
<Resource name="jms/sampleFactory" auth="Container" type="com.sun.messaging.QueueConnectionFactory" description="OpenMQ Queue Connection Factory" factory="com.sun.messaging.naming.CFReflectionObjectFactory" imqAddressList="mq://localhost:7676/jms" imqReconnectEnabled="true" imqReconnectInterval="60000" imqReconnectAttempts="5" />
If you see I have set the host using recommended imqAddressList property and some other reconnection properties just as example (because of reflection any property can be set, any property which exists in the ConnectionConfiguration OpenMQ class of course). With this final setup I present a video where the demo servlet is used to send three text messages to the queue and then they are retrieved.
I hope that I am helping my mate and maybe someone else (possibly it is a bit late, I am sorry). It is incredible there was no information about a Tomcat/OpenMQ setup. In summary there are some ObjectFactories ready to be used in the OpenMQ distribution but I fear that they are a bit old and obsolete, and that is the reason I created a new one which can define any configuration property.
Be smart!
Friday, June 3. 2011
Sound Issue in Linux Adobe Flash (Better Solution)
Yesterday I spent some time reading the fedora bug about the flash issue commented in the previous entry. There are very interesting comments in it, but more important, there are two attachments to replace by yourself the memcpy calls with memmove (one is a bash script and the other is a C++ program).
I tried the C++ source code program and it seems to work quite well:
# wget -O replace.c https://bugzilla.redhat.com/attachment.cgi?id=487982 # g++ -o replace replace.c # cd /usr/lib64/flashplugin-nonfree/ # cp libflashplayer.so libflashplayer.so.ORIG # ~/replace libflashplayer.so Found memmove at symbol table index 516 Found and fixed 1 reloc entries
Of course this solution is a much better approach, because it depends directly on you, you can understand what you are doing and it will work for future plugin releases (if they are still broken). But remember I was desperate at those times. Another thing I did not comment before is that this bug only applies to amd64 linux platforms (not i386, 32 bit installations). And paying more attention to glib package upgrade (I also upgraded my desktop debian box yesterday), I realized there is a warning screen about this memcpy issue you need to accept. I really do not know if I just accepted it without reading in the laptop upgrade or it is new now... I bet for the first option.
Cheerio!
Comments