Friday, September 25, 2009

JMSTester online

The last couple of weeks I have spent some time on site working with ActiveMQ users helping them to get a decent benchmark in place for their JMS layer. I already had some code for a JMS benchmarking framework from earlier assignments and used the chance to get that a bit polished and documented.

Essentially it is designed to run a set of distributed JMS clients with a defined load profile. The framework will then measure the throughput and also allows to measure key performance indicators via JMX or on the OS level during the benchmark. This gives nice hints, whether the JMS server machines are CPU bound, lack IO capacity or have to less memory etc.

For all metrics graphs are created and if you want to run even more sophisticated analysises, all metrics are pushed into a CSV file that can be used in your favorite spreadsheet calculator.

The JMSTest framework is now a public project at FUSE Source.

Have fun trying it out if you want, any feedback is more than welcome.

Wednesday, September 2, 2009

Enabling Security for ActiveMQ web apps

Recently I was working with for a company to harden their set up of Active MQ. Though not the ultimate monitoring solution for Avtive MQ, they wanted to use the web console for providing some kind of ad hoc visibility on key properties. The ActiveMQ web page describes the set up that is pretty straight forward when an external servlet engine is used, but configuring the embedded jetty is a bit different.

From the ActiveMQ web console documentation we could see that we need 3 things:


  1. An User Realm that can be plugged into the Jetty engine.

  2. Credentials definitions.

  3. A security aware web application.



Let's have a look at each of these things:

We start with rewriting the jetty bean definition in the default activemq.xml with the following activemq.xml:






<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:jetty="http://mortbay.com/schemas/jetty/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
http://activemq.apache.org/camel/schema/spring http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">

<!-- Allows us to use system properties as variables in this configuration file -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:///${activemq.base}/conf/credentials.properties</value>
</property>
</bean>

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="TRT_INT" dataDirectory="${activemq.base}/data">

<!-- Destination specific policies using destination names or wildcards -->
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">" producerFlowControl="true"/>
<policyEntry topic=">" producerFlowControl="true"/>
</policyEntries>
</policyMap>
</destinationPolicy>

<!-- Define Virtual Destinations for Topics -->
<!-- All topics will have a corresponding queue corresponding to the name pattern below
where consumers can connect to in order to act as subscription groups. -->
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<virtualTopic name=">" prefix="VTopic.*." />
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>

<!-- predefine Queues corresponding to Virtual topics so that no messages get lost due
to unregistered consumers. -->
<destinations>
<!-- The Subscriber group App1 is the name of a queue that ALL instances of App1
would use to consume messages from the topic "X". All
consumers using the same queue name act as ONE subscriber and the messages
will be load balanced across the group. -->
<!-- queue physicalName="VTopic.App1.X" / -->
</destinations>

<!-- Use the following to configure how ActiveMQ is exposed in JMX -->
<managementContext>
<managementContext createConnector="true"/>
</managementContext>

<!-- The store and forward broker networks ActiveMQ will listen to -->
<networkConnectors>
<!-- by default just auto discover the other brokers -->
<!--<networkConnector name="default-nc" uri="multicast://default"/>-->
<!-- Example of a static configuration:
<networkConnector name="host1 and host2" uri="static://(tcp://host1:61616,tcp://host2:61616)"/>
-->
</networkConnectors>

<!-- Using the Kaha DB store for persistence now -->
<persistenceAdapter>
<kahaDB directory="${activemq.base}/data/kahadb" />
</persistenceAdapter>

<!-- Don't have ssl
<sslContext>
<sslContext keyStore="file:${activemq.base}/conf/broker.ks" keyStorePassword="password" trustStore="file:${activemq.base}/conf/broker.ts" trustStorePassword="password"/>
</sslContext -->

<!-- The maximum about of space the broker will use before slowing down producers -->
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage limit="350 mb"/>
</memoryUsage>
<storeUsage>
<storeUsage limit="20 gb" name="foo"/> <!-- used for persistent messages -->
</storeUsage>
<tempUsage>
<tempUsage limit="100 mb"/> <!-- used for non persistent messages -->
</tempUsage>
</systemUsage>
</systemUsage>


<!-- The transport connectors ActiveMQ will listen to -->
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616"/>
</transportConnectors>
</broker>

<jetty:jetty>
<connectors>
<jetty:nioConnector port="8161"/>
</connectors>
<handlers>
<jetty:webAppContext contextPath="/admin" resourceBase="${activemq.base}/webapps/admin" logUrlOnStart="true"/>
</handlers>
<userRealms>
<bean class="org.mortbay.jetty.security.HashUserRealm">
<property name="name" value="ActiveMQ Realm" />
<property name="config" value="${activemq.base}/conf/webconsole.properties" />
</bean>
</userRealms>
</jetty:jetty>
</beans>



Next you need to create the file webconsole.properties in the conf directory of ActiveMQ.The file contains one line per user with the format






useName: password, [roleName]*



For example:






myAdmin: THESECRET, amqAdmin



Finally you need to amend the web.xml files of the ActiveMQ web applications (${activemq.base}/webapps/*/WEB-INF/web.xml) with the following xml fragment:






<security-constraint>
<web-resource-collection>
<web-resource-name>A Protected Page</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>amqAdminrole-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ActiveMQ Realm</realm-name>
</login-config>



Make sure, the Realm name matches the one you have set in the Realm definition and the role name match roles you have named in the properties file.