Integrating ActiveMQ Web Console

If you build an application that integrates ActiveMQ broker, it makes sense that you want to integrate its web console as well. It provides your users with basic monitoring and management operations. One road you can take is to integrate it as a WAR archive, but as you already have a whole lot of ActiveMQ integrated, you probably want to include just necessary subset of libraries and files and always have most up to date version of it.

This integrations is not as seamless as it probably should be, but here are a couple of steps I found do the job for me (and hope can help someone else with the same requirements). I use Maven2 as a build tool and its assembly plugin to create a final distribution. If you are using some different building environment it is probably the best to stick with customized WAR archive.

First of all, you need to use version 2.2 (or later) of the maven assembly plugin. For that, put the following snippet to the appropriate pom.xml

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2-beta-1</version>
            <configuration>
                <descriptors>
                    <descriptor>
                        src/main/descriptors/unix-bin.xml
                    </descriptor>
                </descriptors>
                <finalName>sensatic-jqr-${pom.version}</finalName>
                <appendAssemblyId>false</appendAssemblyId>
            </configuration>
        </plugin>
    </plugins>
</build>

The version 2.2 of the plugin is needed for some additional WAR extraction manipulations, as we will see in the moment.
Now, let’s see some things we should add to the deployment descriptor. First of all, we need to create a dependency set that will extract the WAR to the specified folder inside our application

<dependencySet>
    <outputDirectory>/webapps/admin</outputDirectory>
        <outputFileNameMapping></outputFileNameMapping>
        <unpack>true</unpack>
        <unpackOptions>
            <excludes>
                <exclude>**/activemq.xml</exclude>
                <exclude>**/webconsole-*.xml</exclude>
                <exclude>WEB-INF/lib/**</exclude>
            </excludes>
        </unpackOptions>
        <scope>runtime</scope>
        <includes>
            <include> org.apache.activemq:activemq-web-console </include>
        </includes>
</dependencySet>

There are a few notes worth commenting about this snippet. As you can see we have configured our assembler to extract war file into /webapps/admin folder. The thing why we needed version 2.2 of the assembly plugin is the usage of <unpackOptions> tag, which allows us to exclude some elements of the WAR archive. I excluded default configuration files, since I will later include just one that I need. Also, I stripped all JARs from the WEB-INF/lib folder. As I said earlier, the most of those libraries are already in the classpath of your application. Those that are not (and are exclusively related to the web) we can include with the following snippet.

<dependencySet>
    <outputDirectory>/webapps/admin/WEB-INF/lib</outputDirectory>
        <scope>runtime</scope>
        <includes>
            <include>opensymphony:sitemesh</include>
            <include>javax.servlet:jstl</include>
            <include>org.mortbay.jetty:jsp-2.1</include>
            <include>org.mortbay.jetty:jsp-api-2.1</include>
            <include>taglibs:standard</include>
            <include>rome:rome</include>
            <include>jdom:jdom</include>
        </includes>
</dependencySet>

My application already starts jetty (to run ActiveMQ Rest/Ajax interface), which means that all appropriate JAR’s are already in application’s classpath. So here, we will include only JARs related to this specific web application. A few more things that I found interesting while making this work are:

  • Web console threw exceptions (can’t find resources such as TLD’s) when some of these libraries were not directly in its classpath (but in parent application’s classpath instead). I’m not sure why this is, but I guess it’s a class loading issue. In this case I wanted to have it specifically in the web application’s classpath (which is the combination that works), so I didn’t investigate this issue further.
  • If web console is using ActiveMQ from its classpath, it throws an exception trying to locate the embedded broker. There are three solutions here: one to remove all ActiveMQ JARs from the WEB-INF/lib (solution used here), tell Jetty to give priority to parent application’s loader (more about this later) or use some other mechanism for configuring web console.

After we set up the classpath, it’s time to deal with the proper configuration of our web console. By default, web console tries to load webconsole-embedded.xml which tries to start a broker and use it. Because we already have our broker started this is totally unnecessary, so we need to provide our own “lightweight” configuration as follows


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

  <bean id="placeholderConfig"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />

  <!-- use the following bean for a local in-JVM broker -->
  <bean id="brokerQuery"
      class="org.apache.activemq.web.SingletonBrokerFacade"
      autowire='constructor' singleton="false"/>


  <bean id="sessionPool"
      class="org.apache.activemq.web.SessionPool">
  	<property name="connectionFactory" ref="connectionFactory"/>
  </bean>

  <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="vm://localhost"/>
  </bean>

  <bean id="queueBrowser"
      class="org.apache.activemq.web.QueueBrowseQuery"
      autowire='constructor' singleton="false"/>
  <bean id="messageQuery"
      class="org.apache.activemq.web.MessageQuery"
      autowire='constructor' singleton="false"/>

</beans>

Finally, the only thing left to do is to configure our web application. If you don’t have Jetty already configured, you’ll have to do that first. In other case, all you need to do is to provide additional web application configurations, such as

<bean class="org.mortbay.jetty.webapp.WebAppContext">
    <property name="contextPath" value="/admin"/>
    <property name="resourceBase" value="webapps/admin"/>
    <property name="parentLoaderPriority" value="false"/>
</bean>

if you are using Spring, or this

<webAppContext contextPath="/admin"
    resourceBase="webapps/admin" parentLoaderPriority="false" />

in case of XBean.
The parentLoaderPriority property tells Jetty whether to give priority to classes from the parent application or the web application, in case it finds duplicate classes. I generally found that giving parent application’s classes a priority could raise problems in case that it uses older versions of the libraries web console needs. For that reason I set it to false.

In the future, I would like to move these libraries from the WEB-INF/lib folder in some folder where all (potential) web applications could share it.
Finally, it would be great if we could grab zipped version from the repository (without libraries and with proper configuration), unzip it and embed in the parent application. It would make this process a breeze.

Leave a comment

Your email address will not be published. Required fields are marked *