10

The java doc here related to Spring CachingConnectionFactory has comment :

NOTE: This ConnectionFactory requires explicit closing of all Sessions obtained from its shared Connection. This is the usual recommendation for native JMS access code anyway. However, with this ConnectionFactory, its use is mandatory in order to actually allow for Session reuse.

I am not clear how to handle this with the below given configuration in my application.

<bean id="springApp" class="com.codereq.springcore.jms.SpringJMSListenerApp"  />

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="jmsConnectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener"/>
    <property name="sessionTransacted" value="true"/>
    <property name="concurrentConsumers" value="5" />
    <property name="maxConcurrentConsumers" value="15" />
</bean>

<bean id="messageListener" class="com.codereq.springcore.jms.MessageListenerApp" />

<bean id="jmsConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"
        p:targetConnectionFactory-ref="emsConnectionFactory"
        p:sessionCacheSize="100" 
        p:cacheConsumers="true" />

<bean id="emsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="GenericConnectionFactory"/>
    <property name="jndiTemplate" ref="jndiTemplate"/>
</bean>


<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
        <props>
            <prop key="java.naming.factory.initial">com.tibco.tibjms.naming.TibjmsInitialContextFactory</prop>
            <prop key="java.naming.provider.url">tibjmsnaming://localhost:7222</prop>
            <prop key="java.naming.security.principal">admin</prop>
            <prop key="java.naming.security.credentials">admin</prop>
        </props>
    </property>
</bean>

<bean id="destination" class="com.tibco.tibjms.TibjmsQueue">
    <constructor-arg value="com.sample.queue" />
</bean>

The listener class is this :

public class MessageListenerApp implements MessageListener {

private static int c = 0;

@Override
public void onMessage(Message arg0) {

    try {
        System.out.println("Received Message..."+arg0.getStringProperty("MessageNum")+". Waiting to finish..");
        Thread.sleep(2000);
        System.out.println("Finished processing.."+arg0.getStringProperty("MessageNum")+".."+(c++));
    } catch (Exception e) {
        e.printStackTrace();
    }

}

}

How do I follow recommendation that Sessions obtained from shared connection should be closed explicitly ?

Came across SessionAwareMessageListener interface which provides onMessage method which gives handle to Session. So to properly implement session closing, should this interface be implemented ?

Gaurav
  • 1,549
  • 2
  • 15
  • 31

2 Answers2

6

It is generally not a good idea to use a caching connection factory with a listener container, especially when using maxConcurrentConsumers > concurrentConsumers - you can end up with cached consumers in the cache, which get messages where there is no listener, and such messages can get "stuck".

So, don't use a CCF in this case, it's really intended for use on the producer side.

Since the container manages concurrency, the sessions/consumers are long-lived and don't need to be cached.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • But still the question seems unanswered. Let the message producer is cached in another scenario. How to handle the explicit closing of the sessions obtained from CCF ? – Manu Oct 15 '13 at 17:08
  • `session.close()` - the javadoc comment is simply saying that the session is only returned to the pool when the client closes it. If you need a PHYSICAL close, then don't use a `CachingConnectionFactory`. Spring clients (JmsTemplate, listener container) do close the connection when appropriate. – Gary Russell Oct 15 '13 at 17:13
  • I have configured a `CachingConnectionFactory` for `JMSTemplate` to use with Tibco EMS. My message producer is running in a tomcat server. Even after the tomcat is gracefully shutdown, the connection is still not destroyed in EMS. Is there a workaround to close it? – Manu Oct 15 '13 at 17:30
  • The connection factory implements `DisposableBean` which means its `destroy()` method will be called (closing the connection) when the application context is destroyed. If it's a normal web application context, that should happen automatically; if you are managing the context's lifecycle yourself, it's up to you to destroy it. – Gary Russell Oct 15 '13 at 20:21
  • Its a normal web application context. context's lifecycle is managed by Spring only. in the blog [Using Spring to Send JMS messages](http://bsnyderblog.blogspot.in/2010/02/using-spring-jmstemplate-to-send-jms.html) I could see that adding the `daemon=true` option to the transport will terminate the connection. But its not woking in my case. – Manu Oct 16 '13 at 06:33
  • I suggest you turn on DEBUG logging for `org.springframework` - it emits copious logging about the bean lifecycle. You should then be able to determine what's preventing the factory from being destroyed. – Gary Russell Oct 16 '13 at 12:38
  • @GaryRussell: So do you suggest that for Spring's `DefaultMessageListenerContainer` and `SimpleMessageListenerContainer` one should always use `SingleConnectionFactory`? – Michał Kowalczyk Feb 23 '16 at 13:12
  • It depends on what you are doing downstream on the container thread - if you are executing `JmsTemplate` sends to the same broker using the same factory, you will likely want to cache producers for performance. The only hard and fast rule is you must __never__ cache consumers if you are using variable concurrency in the container. – Gary Russell Feb 23 '16 at 13:39
  • @GaryRussell should be improved the current javadoc for the `CachingConnectionFactory` class? with the current note in the javadoc gives the impression or seems that something manually should be configured. I did realize that use the Bean's destroyMethod is not valid since `CachingConnectionFactory` class has no a `close` method – Manuel Jordan May 25 '16 at 20:53
  • @ManuelJordan I am not sure what you mean - let's take it off-line - Stack overflow moderators don't like extended commentary on answers. – Gary Russell May 25 '16 at 22:26
0

Application is not required to close the Session when using DefaultMessageListenerContainer, it creates the required sessions and closes them during shutdown.

Per my understanding the note you mentioned applies when Session is created by application using CachingConnectionFactory reference about which spring will have no clue.

Limestone
  • 51
  • 1
  • 3