Clustering using ActiveMQ and Tomcat

I thought someone might be interested in a HSearch setup using ActiveMQ. It turned out be be quite easy and works fine for us.

The following assumes you are using Tomcat and have two web applications you want to configure in a master/slave configuration. An example application could be an advert application where users can create/search adverts in the main application, whereas an administrator can manage the adverts from another admin web application.

 

First step is to make sure that each of the applications has its own Hibernate configuration according to the Hibernate Search documentation.

Next step is to make sure that JNDI is setup properly. Just add the following to the context.xml of your apps. Note that in this example we are using a TCP based broker URL. This is necessary since each of the web applications will include in its WEB_INF/lib their own ActiveMQ dependency. Since each application has its own class loader a VM based broker setup will fail.

 

Alternatively you could put the ActiveMQ dependencies into the shared server lib. For more information about this read this post

    <!-- ActiveMQ ConnectionFactory -->    
    <Resource name="jms/ConnectionFactory" auth="Container" type="org.apache.activemq.ActiveMQConnectionFactory"
        description="JMS Connection Factory" factory="org.apache.activemq.jndi.JNDIReferenceFactory" 
        brokerURL="tcp://localhost:61616?trace=true" brokerName="LocalActiveMQBroker"/>

    <!-- ActiveMQ HibernateSearch queue -->    
    <Resource name="queue/hibernatesearch" auth="Container" type="org.apache.activemq.command.ActiveMQQueue"
        description="Hibernate search queue" factory="org.apache.activemq.jndi.JNDIReferenceFactory"
        physicalName="HibernateSearchController"/>

 

In case you are using maven you can add the following dependency to your pom.xml to get the ActiveMQ dependencies:

   <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-core</artifactId>
      <version>4.1.1</version>
   </dependency>

 

Next you need to start a message broker. You can do this for example in the master application.

        // Start message broker
        try {
            File brokerdata = new File(PropertyReader.getProperty("jmsprovider.datadir"));
            if (! brokerdata.exists()) {
                brokerdata.mkdirs();
            }
            
            BrokerService broker = new BrokerService();
            broker.setDataDirectory(brokerdata);
            broker.addConnector("tcp://localhost:61616");
            broker.start();
        } catch (Exception e) {
            throw new RuntimeException("error during broker startup: " + e);
        }

 

Last step is to implement a subclass of AbstractJMSHibernateSearchController in the master application. This class has to register itself as listener to the queue. The code could look somewhat like this:

                Context initCtx = new InitialContext();

                // Look up for connection factory & create connection, session
                ConnectionFactory connectionFactory = (ConnectionFactory) initCtx.lookup("java:comp/env/jms/ConnectionFactory");       
                connection = connectionFactory.createConnection();
                connection.start();
                javax.jms.Session session = connection.createSession(true, 0);

                // Look up for destination and set listner
                Queue queue = (Queue) initCtx.lookup("java:comp/env/queue/hibernatesearch");
                MessageConsumer consumer = session.createConsumer(queue);
                consumer.setMessageListener(this);

 

Make sure that the above code is executed at application startup, for example by implementing a startup listener.