Random bits of information by a developer

12 September 2009

HornetQ JMS on Jetty

Over the past few days I've been working on getting HornetQ (http://www.hornetq.org) to work on Jetty (http://www.mortbay.org/jetty/) -- specifically the embedded Jetty that Gradle (http://www.gradle.org) uses. I would first like to thank those on the HornetQ irc channel (irc://freenode.net:6667/hornetq) for helping me out and offering advice. Specifically I would like to thank Clebert Suconic and Andy Taylor for looking at some of my code and making suggestions that ultimately led me to this solution. It should be noted that the two solutions I will be discussing here are probably not the only roads to success. I'm quite confident the same could be achieved programmatically by setting up HornetQ in a bean that's loaded at application deploy time or similar fashion.

HornetQ setup using jetty-env.xml

Both of my two solutions are using jetty-env.xml to call the needed setup functions for HornetQ. The same could be achieved with jetty.xml if deploying to a standard install of Jetty. More information about these configuration files can be found at http://docs.codehaus.org/display/JETTY/Configuring+Jetty. In short these files act as an XML means of working with Java objects.  Objects can be instantiated, methods called, etc. All that's being done here is calling the methods in the HornetQ API to startup HornetQ in Embedded mode. Below is the jetty-env.xml I used:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<Configure id="wac" class="org.mortbay.jetty.webapp.WebAppContext">
  <New id="jmsServerManager" class="org.mortbay.jetty.plus.naming.Resource">
    <Arg><Ref id="wac"/></Arg>
    <Arg>jms/serverManager</Arg>
    <Arg>
      <New class="org.hornetq.jms.server.impl.JMSServerManagerImpl">
        <Arg>
          <Call class="org.hornetq.core.server.HornetQ" name="newHornetQServer">
            <Arg>
              <New class="org.hornetq.core.config.impl.ConfigurationImpl">
                <Set name="persistenceEnabled">false</Set>
                <Set name="securityEnabled">false</Set>
                <Get name="AcceptorConfigurations">
                  <Call name="add">
                    <Arg>
                      <New class="org.hornetq.core.config.TransportConfiguration">
                        <Arg>org.hornetq.core.remoting.impl.invm.InVMAcceptorFactory</Arg>
                      </New>
                    </Arg>
                  </Call>
                </Get>
              </New>
            </Arg>
          </Call>
        </Arg>
        <Call name="start" />
        <Call name="createConnectionFactory">
          <Arg>HornetQConnectionFactory</Arg>
          <Arg>
            <New class="org.hornetq.core.config.TransportConfiguration">
              <Arg>org.hornetq.core.remoting.impl.invm.InVMConnectorFactory</Arg>
            </New>
          </Arg>
          <Arg>
            <New class="java.util.ArrayList">
              <Call name="add">
                <Arg>java:comp/env/jms/connectionFactory</Arg>
              </Call>
            </New>
          </Arg>
        </Call>
        <Set name="context">
          <New class="javax.naming.InitialContext">
          </New>
        </Set>
        <Call name="createQueue">
          <Arg>testQueue</Arg>
          <Arg>java:comp/env/jms/queues/testQueue</Arg>
          <Arg></Arg>
          <Arg type="boolean">false</Arg>
        </Call>
      </New>
    </Arg>
  </New>
</Configure>
An instance of JMSServerManager is being created and the needed configuration objects are being created inline in this example. It's very important to make the start call on the JMSServerManager before any other calls on the object to create queues, topics or connection factories are made, otherwise you'll get exceptions in your log saying the server hasn't been started yet. It's also important to make sure the full JNDI names are used. jetty-env.xml is used to bind elements to JNDI for you but because the JMSServerManager will be doing the binding for us I found that the full JNDI name must be used. If programmatic access to the JMSServerManager is needed this is also bound to JNDI for us by Jetty under jms/serverManager -- which doesn't use the full JNDI name because Jetty is handling the binding, a slight bit confusing I know. Work with JMS can now proceed using standard JNDI lookups and JMS code to send and receive messages. All of this code is available at my GitHub repo: http://github.com/LightGuard/Research---Development--JEE-/tree/hornetq feel free to check it out, fork it, whatever :)

HornetQ setup using jetty-env.xml and HornetQ configuration files

This setup is very similar to one above, only using the HornetQ configuration files. Code is located at http://github.com/LightGuard/Research---Development--JEE-/tree/hornetq-with-config-file. Here's the jetty-env.xml file:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<Configure id="wac" class="org.mortbay.jetty.webapp.WebAppContext">
  <New id="jmsServerManager" class="org.mortbay.jetty.plus.naming.Resource">
    <Arg><Ref id="wac"/></Arg>
    <Arg>jms/serverManager</Arg>
    <Arg>
      <New class="org.hornetq.jms.server.impl.JMSServerManagerImpl">
        <Arg>
          <Call class="org.hornetq.core.server.HornetQ" name="newHornetQServer">
            <Arg>
              <New class="org.hornetq.core.config.impl.FileConfiguration">
                <Set name="configurationUrl">hornetq-configuration.xml</Set>
                <Call name="start" />
              </New>
            </Arg>
          </Call>
        </Arg>
        <Arg>hornetq-jms.xml</Arg>
        <Call name="start" />
        <Set name="context">
          <New class="javax.naming.InitialContext">
          </New>
        </Set>
      </New>
    </Arg>
  </New> 
</Configure>
It's much smaller this time as most of the configuration has been moved into the hornetq-configuration.xml and hornetq-jms.xml files (both of which are on GitHub). That's really all there is to it! My next steps will be to get logging of hornetq (and hopefully Jetty) using log4j and same log file as the rest of the application.

4 comments:

Andy Taylor said...

Nice post, it shows that because HornetQ is just a simple set of POJO's it can easily be embedded in any application.

Anonymous said...

Do you have updated instructions for HornetQ 2.2.x? I tried your config in Jetty 8.0.4 with HornetQ 2.2.5, but I get ClassNotFound exception on org.hornetq.core.server.HornetQ. Seems that class is not in 2.2, perhaps last seen in HornetQ 2.0? Any help would be appreciated?

lightguardjp said...

I haven't had a need to do this again, I may get some later to take a look though.

Justin said...

This is as far as I got with HornetQ 2.2.x on Jetty 8.0.x. I have not been able to figure out the connection factory. If anyone wants to use this as a starting point and complete it, be my guest.



jms/serverManager






false
false




org.hornetq.core.remoting.impl.netty.NettyAcceptorFactory






org.hornetq.core.remoting.impl.invm.InVMAcceptorFactory