我遇到一个与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反应?还是在春季可能缺少的东西?
我知道它们有些旧,但是在较新版本的Spring中,找不到适合我情况的任何东西。