1

Context: a Spring 4.0.6 app running inside JBoss EAP 6.2. Part of the application is a JMS queue and on the receiving side, its messages need to be processed in parallel, otherwise many messages take too long.

Spring's JMS listener is configured with a concurrency of 10 and delegates to an executor pool with 10 threads (first question: is this relationship correct?)

<bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="10" />
        <property name="maxPoolSize" value="10" />
        <property name="queueCapacity" value="20" />
    </bean>

    <jms:listener-container destination-resolver="jndiDestinationResolver" destination-type="queue" acknowledge="auto" connection-factory="jmsConnectionFactory" concurrency="10" task-executor="executor">
        <jms:listener destination="java:jboss/exported/jms/queue/myQueue" ref="myMessageHandler"/>
    </jms:listener-container>

Under load, the following error appears frequently:

10:32:47,457 ERROR [org.hornetq.ra] (org.springframework.jms.listener.DefaultMessageListenerContainer#0-112) HQ154002: Could not create session: javax.jms.IllegalStateException: Only allowed one session per connection. See the J2EE spec, e.g. J2EE1.4 Section 6.6
    at org.hornetq.ra.HornetQRASessionFactoryImpl.allocateConnection(HornetQRASessionFactoryImpl.java:811)
    at org.hornetq.ra.HornetQRASessionFactoryImpl.createSession(HornetQRASessionFactoryImpl.java:465)
    at org.springframework.jms.support.JmsAccessor.createSession(JmsAccessor.java:197) [spring-jms-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer.access$1400(DefaultMessageListenerContainer.java:119) [spring-jms-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.initResourcesIfNecessary(DefaultMessageListenerContainer.java:1122) [spring-jms-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1101) [spring-jms-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1094) [spring-jms-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:991) [spring-jms-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_60]

Now, I understand the JMS spec prohibits that. It's not clear how Spring gets in the position to hit the limitation. Particularly, I checked the DefaultMesageListenerContainer source which has the following fragment:

1120    if (this.session == null && getCacheLevel() >= CACHE_SESSION) {
1121        updateRecoveryMarker();
1122        this.session = createSession(getSharedConnection());
1123    }

so Spring appears to attempt to reuse a connection (see line 1122). Is there something missing from the configuration that makes it do this?

wishihadabettername
  • 14,231
  • 21
  • 68
  • 85

1 Answers1

2

I stumbled upon the same issue while trying to use the Generic JMS RA. What made it work correctly in the end was to configure the cache level on the listener container, setting it to "NONE", to avoid creating a new session on an existing connection.

Adding cache="none" to your jms:listener-container should do the trick for your configuration.

Tome
  • 3,234
  • 3
  • 33
  • 36
  • 1
    Or if you are using java config instead of XML, you can call this on your instance of DefultMessageListnerContainer: `setCacheLevel(DefaultMessageListenerContainer.CACHE_NONE)` – ToeBee Aug 28 '15 at 23:56