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?