使用CachingConnectionFactory和Oracle AQ的JMS-131(会话已关闭)

时间:2018-11-14 07:44:02

标签: java spring jms oracle-aq ucp

我遇到一个与Spring-JMS CachingConnectionFactory结合Oracle UCP有关的问题(JMS-131-会话已关闭)。如果缓存的JMS-Session的基础连接被关闭,则CachingConnectionFactory或JmsTemplate似乎都不会按预期做出反应。

实际上很容易收到这样的错误:

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>

出于参数考虑,我将 abandonedConnectionTimeout maxConnectionReuseTime timeoutCheckInterval 设置为3,以强制ConnectionPool关闭所有已关闭的连接有一阵子没用了。

CachingConnectionFactory和JmsTemplate的XML配置:

<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的第一次调用按预期工作,但是第二个调用将失败,并在开头出现Exception。

正在运行的应用程序似乎无法单独从此错误中恢复,因此有必要重新启动该应用程序。

如果在睡眠后调用 CachingConnectionFactory#resetConnection ,则可以使上述测试成功运行。

问题

问题是,JmsTemplate或CachingConnectionFactory的配置是否有问题?

对于消息消费者,Spring-JMS可以使用ExceptionListener-Interface处理这种情况。框架将为Consumer单独调用 CachingConnectionFactory#resetConnection 。但是对于Message-Producer,我找不到类似的东西。

Spring-JMS是否故意不对Message-Producer做出JmsExceptions反应?还是在春季可能缺少的东西?

使用的库和版本

  • 春季-4.2.9
  • UCP-11.1.0.7.0
  • AQ-API-11.1.0.7.0
  • OJDBC7-12.1.0.2

我知道它们有些旧,但是在较新版本的Spring中,找不到适合我情况的任何东西。

0 个答案:

没有答案