We have a Spring based web application deployed in Tomcat. The web application uses Spring's JmsTemplate and DefaultMessageListenerContainer to produce and consume messages respectively. Our JMS provider is ActiveMQ "Classic" and we have used ActiveMQ client libraries to establish a connection with the native broker. We have used PooledConnectionFactory from the activemq-pool library. Since it's a Spring web application we have defined the connection factory bean and wired the connectionFactory into JmsTemplate and DefaultMessageListenerContainer beans respectively. We are assuming the pooling would be driven by Spring through the connection factory.
The behavior we are seeing is the JMS sessions are being created/destroyed continuously. Under load the application would stop consuming the messages.
After reading different articles we are trying to understand the role of JCA JMS. Can anybody suggest implementing JMS through JCA would resolve the issue and to enlist JMS as an XA resource to maintain the connections and sessions through JCA Adapters.
In our Spring JMS web application and Tomcat server we have used both the ActiveMQ PooledConnectionFactory and ActiveMQConnectionFactory from ActiveMQ client libraries. We see the sessions are being created/destroyed frequently and this stops the JMS client from consuming messages.
The below snippet of the Spring beans how we have configured the Spring JmsTemplate and DefaultMessageListenerContainer beans wired in with the connectionFactory.
<!--bean id="jmsQueueConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="ssl://localhost:61616"/>
<property name="trustAllPackages" value="true"/>
</bean-->
<bean id="jmsQueueConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="ssl://localhost:61616"/>
<property name="trustAllPackages" value="true"/>
</bean>
</property>
<property name="maxConnections" value="5"/>
<property name="maximumActiveSessionPerConnection" value="400"/>
</bean>
<bean id="dmDefaultMessageListenerContainer" class="com.crsoftwareinc.crs.core.jmsListener.DMDefaultMessageListenerContainer">
<property name="autoStartup" value="false"/>
<property name="concurrentConsumers" value="1" />
<property name="maxConcurrentConsumers" value="5" />
<property name="cacheLevelName" value="CACHE_NONE"/>
<property name="connectionFactory" ref="jmsQueueConnectionFactory" />
<property name="sessionTransacted" value="true"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsQueueConnectionFactory"/>
<property name="sessionTransacted" value="true"/>
</bean>
We are looking for a work around how could we use JMS with ActiveMQ in Spring Web application in production deployments where the connections and sessions are maintained seamlessly?
Related
I am trying to connect to and consume from two different clusters of rabbitmq using a spring boot app via xml. It works well when a single rabbit:connection-factory bean is created in the application context. However, when the second one is added, it fails to start the application with the error "Parameter 1 of method rabbitListenerContainerFactory in org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration required a single bean, but 2 were found:". How do I go about creating different factories per cluster? Please suggest an alternative way of doing this, if it's not the right approach?
Here is the xml snippet:
<rabbit:connection-factory id="firstConnectionFactory" connection-factory="firstSpringConnectionFactory" />
<rabbit:connection-factory id="secondConnectionFactory" connection-factory="secondSpringConnectionFactory"/>
<bean id="firstSpringConnectionFactory"
class="org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean">
<property name="useSSL" value="${rabbitmq.ssl.enabled}" />
<property name="host" value="${rabbitmq.first.host}"/>
<property name="virtualHost" value="${rabbitmq.vhost}"/>
<property name="port" value="${rabbitmq.cluster.port}"/>
<property name="username" value="${rabbitmq.user}"/>
<property name="password" value="${rabbitmq.first.password}"/>
</bean>
<bean id="secondSpringConnectionFactory"
class="org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean">
<property name="useSSL" value="${rabbitmq.ssl.enabled}" />
<property name="host" value="${rabbitmq.second.host}"/>
<property name="virtualHost" value="${rabbitmq.vhost}"/>
<property name="port" value="${rabbitmq.cluster.port}"/>
<property name="username" value="${rabbitmq.user}"/>
<property name="password" value="${rabbitmq.second.password}"/>
</bean>
And the listener container code:
ConnectionFactory cf = rabbitConnectionFactory;//One of the connnection factories will be injected here from app context
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(cf);
container.setConcurrentConsumers(count);
container.addQueueNames(queueName);
container.setMessageListener(listener);
container.start();
Since you don't rely on the Spring Boot here and don't use Spring AMQP annotation support I suggest you to exclude RabbitAnnotationDrivenConfiguration from auto-configuration:
#EnableAutoConfiguration(exclude={RabbitAnnotationDrivenConfiguration.class})
spring.autoconfigure.exclude = org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration
If you still need #RabbitListener somewhere in other place of your project, you only have a choice to build all the #EnableRabbit infrastructure manually.
My application is running under Websphere Application server and uses apache camel for message routing. My QueueConnectionFactories and Queues are created as JMS resources in the Appserver with jndi pointing to WebsphereMQ Queue Manager. Can I use SpringCachingConnectionFactory for better performance inside JEE container like WAS with JNDI. Following spring config:
<bean id="jmsDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
</bean>
<bean id="myJndiObjectFacory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jms/MyQCF"/>
</bean>
<bean id="myJmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="myJndiObjectFacory"/>
<property name="transactionManager" ref="txManager"/>
<property name="destinationResolver" ref="jmsDestinationResolver"/>
Will above code have same performance as of spring CachingConnectionFactory ?
or
Is it possible to use CachingConnectionFactory with Jndi lookup in websphere JEE container?
I am trying to configure a JMS connection pool in spring/camel for Websphere MQ. I am seeing class cast exception, when tried to use CachingConnectionFactory from spring. Could not find a pool from WMQ, have anybody done connection pooling with WMQ, i didnt find any examples. There are lot of examples for ActiveMQ.
here is what i have so far, that is producing class cast exception.
<bean id="inCachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="inboundMqConnectionFactory1" />
<property name="sessionCacheSize" value="5" />
</bean>
<bean id="inboundWebsphereMq1" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="inCachingConnectionFactory" />
<property name="destinationResolver" ref="jmsDestinationResolver" />
<property name="transacted" value="true" />
<property name="transactionManager" ref="txManager1" />
</bean>
<bean id="inboundMqConnectionFactory1" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="${isi.inbound.queue.host2}" />
<property name="port" value="${isi.inbound.queue.port}" />
<property name="queueManager" value="${isi.inbound.queue.queuemanager2}" />
<property name="channel" value="${isi.inbound.queue.channel2}" />
<property name="transportType" value="${isi.queue.transportType}" />
</bean>
The exception i see is here
trying to recover. Cause: com.sun.proxy.$Proxy37 cannot be cast to com.ibm.mq.jms.MQQueueSession
In general:
do not use QueueConnectionFactory or
TopicConnectionFactory, as ConnectionFactory (JMS 1.1) is replacement for both
Each ConnectionFactory from v7 WMQ JMS client jars provide caching logic on each own so in general you don't need CachingConnection Factory.
Now try it this way:
<bean id="mqConnectionFactory" class="com.ibm.mq.jms.MQConnectionFactory"
p:queueManager="${QM_NAME}"
p:hostName="${QM_HOST_NAME}"
p:port="${QM_HOST_PORT}"
p:channel="${QM_CHANNEL}"
p:clientID="${QM_CLIENT_ID}">
<property name="transportType">
<util:constant static-field="com.ibm.msg.client.wmq.WMQConstants.WMQ_CM_CLIENT" />
</property>
</bean>
<bean id="userConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter"
p:targetConnectionFactory-ref="mqConnectionFactory"
p:username="${QM_USERNAME}"
p:password="${QM_PASSWORD}" />
<!-- this will work -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="userConnectionFactory"
p:cacheConsumers="true"
p:reconnectOnException="true" />
Of course you can cache sessions instead of consumers if you want it that way. By my experience WMQ session caching is measurable performance improvement but only if you are limited on CPU power on WMQ machine or by actual message throughput; both situations are rare in majority of world applications. Caching consumers avoids excessive MQ OPEN calls which is expensive operation on WMQ so it helps too.
My rule of the thumb is consumer + session caching performance benefit is equal to 1/2 of performance benefit of connection caching and usually not wort of pursuing in your everyday JEE stack unless you are hardware limited.
Since WMQ v7, asynchronous consumers are realy realy fast with literally no CPU overhead when compared to spring MC, and are preferred way of consuming messages if you are HW limited. Most of the days I still use Spring as I prefer its easy-going nature.
Hope it helps.
I have configured (with spring) my application to listen to a jms que with activemq, and everything works fine.
My activemq server is installed on another server and sometime it can go offline and I would like to handle the connection error. Is that possible?
Here is my spring configuration
<amq:connectionFactory id="jmsFactory" brokerURL="tcp://xxx.xxx.xxx.xxx:61616" />
<bean id="messageConverter" class="com.unic.thesting.main.jms.message.TheStingMessageConverter" scope="tenant"/>
<jms:listener-container concurrency="10" connection-factory="thestingJmsFactory" destination-type="queue" message-converter="thestingMessageConverter">
<jms:listener destination="in" ref="orderStatusConsumer" method="consume"/>
</jms:listener-container>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" scope="tenant">
<property name="messageConverter" ref="messageConverter" />
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.SingleConnectionFactory" scope="tenant">
<property name="targetConnectionFactory">
<ref local="jmsFactory" />
</property>
</bean>
</property>
</bean>
The DefaultMessageListenerContainer which gets registered when you use ` handles recovering connections to the JMS provider if it gets dropped for any reason (it by default retries every 5 seconds until the connection is restored), so you don't have to do anything on the listener front.
On the sending side with jmsTemplate, you would receive a runtime org.springframework.jms.JmsException if there is any issues in sending a message. You should be able to catch it for any custom processing
In the past, I've been able to successfully connect Camel to a Message Queue exposed as a JNDI resource in WebSphere [1]. This works with a Connection Factory. Great.
Now today, I have a situation where sys admins have only provided an Activation Specification. No Connection Factory. Here's what we've got:
SIBus name: ________
Provider endpoint: ________
Topic space: Default.Topic.Space
Messaging Engine Name: ________
Topic name: ________
From what I've read, Activation Specification is intended for MDBs (Message Driven Beans). But Spring has some APIs for ActivationSpec -- DefaultJmsActivationSpecFactory, for example -- so I'm optimistic I can configure Camel / Spring to work with an Activation Specification. I'm not sure that it matters, but this is an SIBus with a foreign bus.
My question is:
Has anyone had any luck configuring Camel to communicate with an Activation Specification?
See also:
What's the difference between ActivationSpec and ConnectionFactory?
Spring JMS and WebSphere
Spring Support for JCA Message Endpoints
[1] For reference, here's our hard-won Camel config that connects Camel to a Message Queue via a JNDI resource (Connection Factory). Since we had to piece this config together with almost no documentation, I'm hoping a similar config can be done for Activation Spec.
<jee:jndi-lookup id="myTargetConnectionFactory" jndi-name="${mq.jndi-name}"/>
<bean id="jmsDestResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver"/>
<bean id="myConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="myTargetConnectionFactory"/>
<property name="username" value="${mq.username}"/>
<property name="password" value=""/>
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="myConnectionFactory" />
<property name="destinationResolver" ref="jmsDestResolver" />
<property name="concurrentConsumers" value="1" />
<property name="maxConcurrentConsumers" value="10" />
<property name="cacheLevelName" value="CACHE_NONE" />
</bean>