Producer and Consumer in Spring Jms use same connection factory? - java

I have a component which reads messages from a queue and meanwhile sends the processed messages to another queue. Therefore, this component is both a message consumer and producer. For configuring them, I need a connection factory for consuming and another connection factory for the producing. Here is part of the spring configuration.
<!-- Configuration for listener -->
<bean id="mdc.TargetConnectionFactory4Listener" class="com.tibco.tibjms.TibjmsConnectionFactory">
<property name="serverUrl" value="tcp://localhost:7222"/>
</bean>
<bean id="mdc.ConnectionFactory4Listener" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="mdc.TargetConnectionFactory"/>
<property name="username" value="admin" />
<property name="password" value="test" />
</bean>
<bean id="mdc.InputQueue" class="com.tibco.tibjms.TibjmsQueue">
<constructor-arg>
<value>INPUT_QUEUE</value>
</constructor-arg>
</bean>
<bean id="mdc.JmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="mdc.ConnectionFactory4Listener" />
<property name="destination" ref="mdc.InputQueue" />
<property name="messageListener" ref="mdc.MessageReceiver" />
......
</bean>
<!-- Configuration for sender -->
<bean id="mdc.TargetConnectionFactory4Sender" class="com.tibco.tibjms.TibjmsQueue">
<property name="serverUrl" value="tcp://localhost:7222"/>
</bean>
<bean id="mdc.CachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="mdc.TargetConnectionFactory4Sender" />
<property name="sessionCacheSize" value="50" />
</bean>
<bean id="mdc.OutputQueue" class="com.tibco.tibjms.TibjmsQueue">
<constructor-arg>
<value>DISCOVERY_QUEUE</value>
</constructor-arg>
</bean>
<bean id="mdc.JmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="mdc.CachingConnectionFactory" />
</bean>
<bean id="mdc.MessageReceiver" class="net.siemens.discovery.queue.QueueMessageListener">
<property name="jmsTemplate" ref="mdc.JmsTemplate" />
<property name="destination" ref="mdc.OutputQueue" />
......
</bean>
The two queues are running on the same EMS server. Some has opinions about this configurations: they can be configured by using only one ConnectionFactory and two instances are not necessary. However, if I use one ConnectionFactory instance, then the instance is used both in the DefaultMessageListenerContainer and the CachingConnectionFactory (further used in JmsTemplate). I don't know whether they have impact on each other.

It is perfectly normal to use a single connection factory; it is very unusual to use 2 factories in this case.
In fact, if you want to perform JmsTemplate operations on the container thread and you want the interactions to run in a transaction (sessionTransacted = true in the container), then you must use the same connection factory. This allows everything to roll back if there's an exception.
When using a caching connection factory in the listener container you should set the connection factory cacheConsumers to false. (See this answer for more information.

Related

How to start DefaultMessageListenerContainer?

I have written a Spring application for consuming messages using MDP which uses DefaultMessageListnerContainer. I wrote a messageListner and passed it to the property of messageListener to DMLC.
My Spring configuration is below
<!-- ConnectionFactory Definition -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="amqConnectionFactory" />
</bean>
<!-- Message Receiver Definition -->
<bean id="messageReceiver" class="com.practice.MessageReceiver.MyMessageConsumer">
</bean>
<bean id="actualQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="ActualQueue" />
</bean>
<bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="actualQueue" />
<property name="messageListener" ref="messageReceiver" />
</bean>
I want to know how to start this DMLC? I deployed it in Jboss 6.1. But its not consuming messages. I am missing something which I am not able to get by searching for an answer.

JMS Difference between transacted flag and acknowledgement mode

Message producer component has this :
session = connection.createSession(false,
Session.CLIENT_ACKNOWLEDGE);
producer = session.createProducer(session.createQueue(queueName));
I am using AWS SQS to send messages async
I was under the assumption that the message will be in the queue until it is acknowledged explicitly.(because of the client acknowledgement mode)
However it is removed off the queue.
Message consumer component:
I have written an async consumer to pull messages from the queue.
Here is the spring config file;
<bean id="ConnectionFactoryBuilder"
class="com.amazon.sqs.javamessaging.SQSConnectionFactory$Builder">
<property name="regionName" value="us-east-1" />
<property name="numberOfMessagesToPrefetch" value="1" />
<property name="awsCredentialsProvider" ref="CredentialsProviderBean" />
</bean>
<bean id="ConnectionFactory" class="com.amazon.sqs.javamessaging.SQSConnectionFactory"
factory-bean="ConnectionFactoryBuilder" factory-method="build" />
<bean id="Connection" class="javax.jms.Connection" factory-bean="ConnectionFactory"
factory-method="createConnection" init-method="start" destroy-method="close" />
<bean id="QueueName" class="java.lang.String">
<constructor-arg value="${sqs.queueName}" />
</bean>
<bean id="amazonMessageListener" class="com.myapp.AsyncMessageListener" />
<bean id="messageListener"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="amazonMessageListener" />
<property name="defaultListenerMethod" value="onMessage" />
<property name="messageConverter">
<null />
</property>
</bean>
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="ConnectionFactory" />
<property name="destinationName" ref="QueueName" />
<property name="messageListener" ref="messageListener" />
<property name="sessionAcknowledgeMode" value="2"/>
</bean>
i configured my consumer with client acknowledgemode using the property sessionAcknowledgeMode. But still the message is removed off the queue without the consumer explicitly acknowledging using message.acknowledge()
Per AbstractMessageListenerContainer (super class of DefaultMessageListenerContainer) doc:
"sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE": Automatic message acknowledgment after successful listener execution; best-effort redelivery in case of a user exception thrown as well as in case of other listener execution interruptions (such as the JVM dying).
So when using DefaultMessageListenerContainer, if onMessage() method in your listener returns without exception, spring acknowledges the message automatically.

JmsTemplate - define concurrency per queue?

So far i've only been able to find concurrency setting in the jms connection factory:
<jms:listener-container connection-factory="myConnectionFactory"
task-executor="myTaskExecutor"
destination-resolver="myDestinationResolver"
transaction-manager="myTransactionManager"
concurrency="10">
Is it possible to configure the number of consumers for a single queue? i.e something like:
<jms:listener destination="playerStatsQueue" ref="playerStatsService"
method="onMessage" concurrency="100" />
Thanks!~
Do not use the namespace but an abstract parent DefaultMessageListenerContainer and create one child instance per listener. That way you can tweak all the properties you need.
<bean id="parentContainer" abstract="true"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="messageListener" ref="messageListener"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
<bean id="playerStatsListener parent="parentContainer">
<property name="destination" ref="playerStatsQueue"/>
<property name="listener" ref="playerStatsService"/>
<property name="concurrency" value="100"/>
</bean>

Why is an entity manager used in a JMS MessageListener not taking part in the JTA transaction?

I have a Spring configured web app that receives JMS messages via the following listener:
public class EntityPersister implements MessageListener {
#Resource
private EntityManager entityManager;
#Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
Object entity = createEntity(textMessage);
entityManager.persist(entity);
entityManager.flush(); //for debugging only
}
}
}
When I execute this listener in my application I get a NoTransactionException from the line entityManager.flush().
What do I need to configure so that the entity manager takes part in the already existing JTA transaction?
I already tried #Transactional on the above implementation but with no success.
ActiveMQ is used as the JMS provider. The spring configuration is:
<bean id="jmsConnectionFactory" class="com.atomikos.jms.AtomikosConnectionFactoryBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="atomikos-activemq" />
<property name="xaConnectionFactory">
<!-- ActiveMQ wird als JMS Provider genutzt -->
<bean id="activeMQXAConnectionFactory"
class="org.apache.activemq.spring.ActiveMQXAConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
</property>
<property name="maxPoolSize" value="2" />
<property name="localTransactionMode" value="false" />
</bean>
<bean id="entityPersister" class="EntityPersister" />
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="destinationName" ref="entityDestinationName" />
<property name="messageListener" ref="entityPersister" />
<property name="sessionTransacted" value="true" />
<property name="transactionManager" ref="txManager" />
</bean>
OpenJPA is used as the JPA Provider. The persistence unit is:
<persistence-unit name="somePU" transaction-type="JTA">
<jta-data-source>managedDataSource</jta-data-source>
<non-jta-data-source>nonManagedDataSource</non-jta-data-source>
<!-- some entity class listed here -->
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" />
</properties>
</persistence-unit>
The spring configuration is:
<!-- Construct Atomikos UserTransactionManager, needed to configure Spring -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<!-- when close is called, should we force transactions to terminate or
not? -->
<property name="forceShutdown" value="true" />
</bean>
<!-- Also use Atomikos UserTransactionImp, needed to configure Spring -->
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>
<!-- Configure the Spring framework to use JTA transactions from Atomikos -->
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
</bean>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="somePU" />
<property name="jpaPropertyMap">
<map>
<entry key="openjpa.ManagedRuntime" value="jndi" />
</map>
</property>
</bean>
<bean id="entityManager" factory-bean="entityManagerFactory"
factory-method="createEntityManager" />
OpenJPA looks up the XA and non-XA datasource from the persistence unit via JNDI.
If you are using setMessageListener(), the message reception in onMessage happens asynchronously in a separate thread.
The injection of the entityManager happens in a different thread. Normally a transaction is thread-bound. So the injection thread has probably already done its work, and its associated transaction was closed, before message reception starts. Even if that transaction still existed, if would not have been enlisted in the same global transaction used by the message-receiving thread.
You could verify it by creating/getting an entityManager in onMessage explicitly. This way all XA transaction-aware resources should enlist within the same global transaction, because all of these are opened from the same thread.

Restart server without losing JMS connection

I have two app servers. One sends messages to the other using JMS. The sender runs on Tomcat, with ActiveMQ used inside a Spring Framework template method call. The receiver runs on Jetty.
The problem is that when I restart one of the servers-- the receiver of the messages-- after it restarts it doesn't get any more JMS messages until the other server is also restarted. I'm not sure why this is happening. Is there a way to only require a reboot of one server?
The sender XML configuration:
<bean id="producerJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<bean
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="jmsFactory"/>
</bean>
</property>
</bean>
<bean id="simulationMessageSender" class="com.forio.simulate.activemq.SimulationMessageSenderImpl">
<constructor-arg ref="producerJmsTemplate"/>
</bean>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue"
autowire="constructor">
<constructor-arg value="com.forio.simulate.activemq"/>
</bean>
The sender java code:
public void send(final SimulationMessage simulationMessage) throws JMSException
{
jmsTemplate.send(destination, new MessageCreator()
{
public Message createMessage(Session session) throws JMSException
{
Log.debug("Sending message: " + simulationMessage);
ObjectMessage message = session.createObjectMessage(simulationMessage);
message.setStringProperty("simulationMessage", "true");
return message;
}
});
}
The receiver XML configuration:
<bean id="brokerContainer" class="org.apache.activemq.xbean.BrokerFactoryBean">
<property name="config" value="classpath:activemq.xml" />
</bean>
<bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${jms.url}" />
</bean>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue"
autowire="constructor">
<constructor-arg>
<value>com.forio.simulate.activemq</value>
</constructor-arg>
</bean>
<bean id="messageListener"
class="com.forio.simulate.activemq.SimulationMessageListener">
<property name="messageConverter">
<bean
class="com.forio.simulate.activemq.converter.SimulationMessageConverter" />
</property>
<property name="simulationMessageService" ref="simulationMessageService"/>
</bean>
<bean
class="org.springframework.jms.listener.SimpleMessageListenerContainer" init-method="start">
<property name="connectionFactory" ref="jmsFactory" />
<property name="destination" ref="destination" />
<property name="concurrentConsumers" value="3" />
<property name="messageListener" ref="messageListener" />
</bean>
<bean id="consumerJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory" />
</bean>
It is because you are using SingleConnectionFactory. each time SingleConnectionFactory.createConnection() is called, it returns the same connection, even after it is closed by restarting your receiving server.
Maybe CachingConnectionFactory will suite your needs as it has some recover on exception mechanism.

Categories

Resources