JMS-131 (сессия закрыта) с CachingConnectionFactory и Oracle AQ - PullRequest
0 голосов
/ 14 ноября 2018

Я столкнулся с проблемой (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 я не смогнайди что-нибудь, что поможет в моей ситуации.

...