You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »

Java Message Service (JMS) is wide spread and popular messaging java interface. As far as JMS is standardized in JEE, the same application code can successfully work with different JMS implementations: WS MQ, Active MQ, Tibco, Joram, BEA WebLogic, OpenJMS.
CXF provides a transport that enables endpoints to use JMS queues and topics.

Default CXF consumer and procuder using JMS

Implementing CXF client and service using JMS transport is trivial.
Basically, it is enough to configure two things in WSDL:
a) specify jms transport URI in binding element;
b) define jms address in port element.
WSDL binding and port should look like:

<wsdl:binding name="Greeter_SOAPBinding" type="tns:Greeter">
        <soap:binding style="document" transport="http://cxf.apache.org/transports/jms"/>
…
</wsdl:binding>

<wsdl:service name="JMSGreeterService">
        <wsdl:port binding="tns:JMSGreeterPortBinding" name="GreeterPort">
            <jms:address
                destinationStyle="queue"
                jndiConnectionFactoryName="ConnectionFactory" 
jndiDestinationName="dynamicQueues/test.cxf.jmstransport.queue">
               <jms:JMSNamingProperty name="java.naming.factory.initial" value="org.apache.activemq.jndi.ActiveMQInitialContextFactory"/>
                  <jms:JMSNamingProperty name="java.naming.provider.url" value="tcp://localhost:61616"/>
           </jms:address>
        </wsdl:port>
 </wsdl:service>

CXF client and server implemented in java or using Spring configuration magically work for this WSDL (under the hood CXF selects correct JMS Conduit and Destination based on address URL).
Details are described in http://cxf.apache.org/docs/jms-transport.html. CXF also delivers jms_pubsub and jms_pubsub examples illustrating using JMS transport with default settings for ActiveMQ.

Scalability problems

Unfortunately there are two main scalability drawbacks in default JMS configuration:

  1. It doesn't provide sessions pooling and consumers/producers cache.
  2. Default JMS message consumer is single threaded. It means that only one thread will get messages from the queue or topic and pass them to further processing.

Both aspects are critical for enterprise application and their implementation is not easy task. Is there any solution? Yes: Spring JMS functionality and CXF Features. Let discuss them in detail.

Spring JMS functionality

Spring provides a number of useful classes that helps to implement scalable JMS application. Important for us are: org.springframework.jms.connection.CachingConnectionFactory
org.springframework.jms.listener.DefaultMessageListenerContainer

CachingConnectionFactory

CachingConnectionFactory provides sessions pool, consumers and producers cache. Bellow is a sample configuration of CachingConnectionFactory:

<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
	<property name="targetConnectionFactory">
		<bean class="org.apache.activemq.ActiveMQConnectionFactory">
			<property name="brokerURL" value="tcp://localhost:61616" />
		</bean>
	</property>
	<property name="sessionCacheSize" value="20"/>
	<property name="cacheProducers" value="true"/>
	<property name="cacheConsumers" value="true"/>
</bean>

As you can see it is possible to set size of session pool and switch on producers and consumers caching.

DefaultMessageListenerContainer

DefaultMessageListenerContainer enables getting messages from the destination in parallel, using multiple threads.
Configuration of DefaultMessageListenerContainer looks like:

<bean id="queueContainerListener"
	class="org.springframework.jms.listener.DefaultMessageListenerContainer">
		<property name="connectionFactory" ref="connectionFactory" />
		<property name="destinationName" value="Q_WM_OUT" />
		<property name="messageListener" ref="simpleListener" />
		<property name="cacheLevel" value="3" />
		<property name="concurrentConsumers" value="10" />
		<property name="maxConcurrentConsumers" value="50" />
</bean>	

It is possible to define initial and maximal number of concurrent message consumer threads, cache level (3- cache consumers, 2 – cache session, 1 – no caching), specify message listener class (implementing MessageListener interface) and connection factory.
You can see that Spring provides solution for both mentioned scalability aspects. But how we can use it in CXF?

CXF Features

As far as CXF JMS implementation is Spring based, user can benefit from described Spring JMS functionality. CXF provides a possibility to configure different Spring aspects in JMS transport using Features. A Feature is something that is able to customize a Server, Client, or Bus, typically adding capabilities. In our case we will add Feature in ws:endpoint and ws:client to tune JMS transport.

Server configuration

<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
	<property name="targetConnectionFactory">
		<bean class="org.apache.activemq.ActiveMQConnectionFactory">
			<property name="brokerURL" value="tcp://localhost:61616" />
		</bean>
	</property>
	<property name="sessionCacheSize" value="20"/>
	<property name="cacheConsumers" value="true"/>
</bean>

<bean id="jmsConfig" class="org.apache.cxf.transport.jms.JMSConfiguration"
		p:connectionFactory-ref="cachingConnectionFactory"
		p:cacheLevel="3" 
		p:concurrentConsumers="16"
		p:maxConcurrentConsumers="16"
		p:targetDestination="Q_HSC"
		p:wrapInSingleConnectionFactory="false"
	/>

<jaxws:endpoint id=" JMSGreeterService" address="jms://"
		implementor="#JMSGreeterServiceImpl">
		<jaxws:features>
			<bean class="org.apache.cxf.transport.jms.JMSConfigFeature">
				<p:jmsConfig-ref="jmsConfig">
			</bean>
		</jaxws:features>
</jaxws:endpoint>

You can see that endpoint configuration containing JMSConfigFeature feature that has a JMSConfiguration property. JMSConfiguration supports all settings that we have seen in Spring DefaultMessageListenerContainer: cached connection factory with session pool size, number of concurrent consumers, cache level. All settings of JMSConfiguration are described in details in http://cxf.apache.org/docs/using-the-jmsconfigfeature.html.
Using this configuration server application can be tuned to achieve optimal performance in target environment.

Client configuration

<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
	<property name="targetConnectionFactory">
		<bean class="org.apache.activemq.ActiveMQConnectionFactory">
			<property name="brokerURL" value="tcp://localhost:61616" />
		</bean>
	</property>
	<property name="sessionCacheSize" value="20"/>
	<property name="cacheProducers" value="true"/>
</bean>

<bean id="jmsConfig" class="org.apache.cxf.transport.jms.JMSConfiguration"
p:connectionFactory-ref="connectionFactory" p:targetDestination="Q_HSC"
		p:wrapInSingleConnectionFactory="false" /> 

<jaxws:client id="JMSGreeterService" address="jms://"
	serviceClass="com.sopera.services.tpoc.eventgenerator.EventGenerator”>
		<jaxws:features>
			<bean class="org.apache.cxf.transport.jms.JMSConfigFeature">
				<property name="jmsConfig" ref="jmsConfig"/>
			</bean>
		</jaxws:features>
	</jaxws:client>

Client configuration looks very similar to the server one except two things:

  1. CachingConnectionFactory activates producers caching instead consumers caching;
  2. JMSConfiguration hasn’t concurrent consumers settings: client concurrency is under application control and can be implemented using standard Java concurrency API.

h2 Conclusion
It is possible to achieve scalability of CXF client and service using Spring JMS functionality and CXF JMS Configuration Feature.
It is not necessary to write any line of code, just configure and leverage already existing stuff.

References

  1. CXF JMS Transport: http://cxf.apache.org/docs/jms-transport.html
  2. CXF Features: http://cxf.apache.org/docs/features.html
  3. Spring JMS functionality: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jms.html
  • No labels