Я столкнулся с проблемой (JMS-131 - сессия закрыта) относительно Spring-JMS CachingConnectionFactory в сочетании с Oracle UCP.Кажется, что либо CachingConnectionFactory, либо JmsTemplate не реагируют должным образом, если базовое соединение кэшированной JMS-сессии закрывается.
На самом деле легко получить ошибку, подобную этой:
org.springframework.jms.IllegalStateException: JMS-131: Session is closed; nested exception is javax.jms.IllegalStateException: JMS-131: Session is closed
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:279)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:169)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:497)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:580)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:668)
at com.example.jms.SessionClosedTest.testJms131(SessionClosedTest.java:171)
...
Caused by: javax.jms.IllegalStateException: JMS-131: Session is closed
at oracle.jms.AQjmsError.throwIllegalStateEx(AQjmsError.java:471)
at oracle.jms.AQjmsSession.checkSessionStarted(AQjmsSession.java:4450)
at oracle.jms.AQjmsSession.getDBConnection(AQjmsSession.java:4392)
at oracle.jms.AQjmsSession.getAQOwner(AQjmsSession.java:6447)
at oracle.jms.AQjmsSession.createQueue(AQjmsSession.java:1387)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.jms.connection.CachingConnectionFactory$CachedSessionInvocationHandler.invoke(CachingConnectionFactory.java:386)
at com.sun.proxy.$Proxy73.createQueue(Unknown Source)
at org.springframework.jms.support.destination.DynamicDestinationResolver.resolveQueue(DynamicDestinationResolver.java:84)
at org.springframework.jms.support.destination.DynamicDestinationResolver.resolveDestinationName(DynamicDestinationResolver.java:58)
at org.springframework.jms.support.destination.JmsDestinationAccessor.resolveDestinationName(JmsDestinationAccessor.java:98)
at org.springframework.jms.core.JmsTemplate.access$200(JmsTemplate.java:90)
at org.springframework.jms.core.JmsTemplate$4.doInJms(JmsTemplate.java:583)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:494)
... 33 more
Конфигурация приложения
Я настраиваю пул соединений следующим образом:
<bean id="appDataSource" class="oracle.ucp.jdbc.PoolDataSourceFactory" factory-method="getPoolDataSource"
lazy-init="true">
<property name="connectionFactoryClassName" value="oracle.jdbc.pool.OracleDataSource"/>
<property name="user" value="${database.username}"/>
<property name="password" value="${database.password}"/>
<property name="URL" value="${database.url}"/>
<property name="initialPoolSize" value="5"/>
<property name="minPoolSize" value="2"/>
<property name="maxPoolSize" value="20"/>
<property name="connectionWaitTimeout" value="10"/>
<property name="inactiveConnectionTimeout" value="180"/>
<property name="timeToLiveConnectionTimeout" value="0"/>
<property name="abandonedConnectionTimeout" value="3"/>
<property name="maxConnectionReuseTime" value="3"/>
<property name="maxConnectionReuseCount" value="0"/>
<property name="validateConnectionOnBorrow" value="true"/>
<property name="maxStatements" value="80"/>
<property name="timeoutCheckInterval" value="3"/>
<property name="connectionProperties">
<props merge="default">
<prop key="oracle.net.disableOob">true</prop>
<prop key="oracle.net.CONNECT_TIMEOUT">10000</prop>
<prop key="oracle.jdbc.ReadTimeout">12000</prop>
</props>
</property>
</bean>
Для аргументов я устанавливаю leftonedConnectionTimeout , maxConnectionReuseTime и timeoutCheckInterval до 3, чтобы заставить ConnectionPool закрыть все соединения, которые не использовались некоторое время.
XML-конфигурация CachingConnectionFactory и JmsTemplate:
<bean id="jmsConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="sessionCacheSize" value="4"/>
<property name="targetConnectionFactory">
<bean class="com.example.jms.oracleaq.OracleAqConnectionFactoryBean">
<property name="datasource" ref="appDataSource"/>
</bean>
</property>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
<property name="sessionAcknowledgeMode" value="0"/>
<property name="sessionTransacted" value="true"/>
<property name="deliveryPersistent" value="true"/>
<property name="explicitQosEnabled" value="true"/>
<property name="defaultDestinationName" value="SAMPLE_QUEUE"/>
</bean>
Реализация OracleAqConnectionFactoryBean
public class OracleAqConnectionFactoryBean implements FactoryBean<QueueConnectionFactory> {
...
public QueueConnectionFactory getObject() throws Exception {
return oracle.jms.AQjmsFactory.getQueueConnectionFactory(datasource);
}
...
}
Настройка теста
Тест на провоцирование ошибки JMS-131 выглядит следующим образом:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = { "/resources/spring/jpa.xml" })
public class SessionClosedTest {
@Autowired
private JmsTemplate jmsTemplate;
@Test
public void testRF242() throws Exception {
MyBusinessObject someObj = new MyBusinessObject ();
this.jmsTemplate.convertAndSend(someObj);
Thread.sleep(15000);
this.jmsTemplate.convertAndSend(someObj);
}
}
Первый вызов convertAndSend работает, как и ожидалось, однако второй вызов завершится с ошибкой в начале.
Похоже, что работающее приложение не может самостоятельно восстановиться после этой ошибки, поэтому необходимо перезапустить это приложение.
Можно успешно запустить приведенный выше тест, если вы вызовете CachingConnectionFactory # resetConnection после сна.
Вопросы
Вопрос в том, есть ли что-то не так с конфигурацией JmsTemplate или CachingConnectionFactory?
Для Message-Consumer Spring-JMS может справиться с этой ситуацией с помощью ExceptionListener-Interface.Платформа вызовет CachingConnectionFactory # resetConnection для Потребителя.Однако для Message-Producer я не смог найти ничего подобного.
Является ли Spring-JMS преднамеренно собственной реакцией на исключения JmsException для Message-Producer?Или это то, что может отсутствовать в Spring?
Используемые библиотеки и версии
- Spring - 4.2.9
- UCP - 11.1.0.7.0
- AQ-API - 11.1.0.7.0
- OJDBC7 - 12.1.0.2
Я знаю, что они несколько старые, однако в более новых версиях Spring я не смогнайди что-нибудь, что поможет в моей ситуации.