无法在重新部署时查找JNDI资源

时间:2014-02-17 11:22:48

标签: java spring maven tomcat spring-jms

问题:

我的Web应用程序侦听队列上的JMS消息并处理消息。我编写了一些要在CI上运行的测试用例,以确认是否正确接收和处理了消息。 CI构建代码并在运行测试之前将其部署到服务器。

这是服务器使用的配置。

Spring配置                        

<!-- JMS message listener -->
<bean id="someMessageListener" class="com.whatever.MyListener" />

<!-- Cached Connection Factory -->
<bean id="cachedConnectionFactory"
    class="org.springframework.jms.connection.CachingConnectionFactory">
    <property name="targetConnectionFactory" ref="activeMQConnectionFactory"></property>
    <property name="sessionCacheSize" value="10"></property>
    <property name="reconnectOnException" value="true"></property>
</bean>

<!-- Spring message listener -->
<jms:listener-container container-type="default"
    connection-factory="cachedConnectionFactory" acknowledge="client"
>
    <jms:listener destination="DESTINATION" ref="someMessageListener"
        method="onMessage" id="jmsSomeMessageListener" />
</jms:listener-container>

MyListener类中的代码查找JNDI数据库资源以获取sql Connection。进一步处理JMS消息依赖于获得有效的sql连接。 Tomcat使用Resource中的context.xml定义来处理JNDI资源的绑定。

当我第一次在CI上运行代码时,代码工作正常。当我使用maven目标tomcat:undeploytomcat:deploy重新部署WAR时,我的消息侦听器将无法再获取数据库连接。我得到以下异常

例外

java.lang.RuntimeException: 
javax.naming.NameNotFoundException: 
Name [comp/env/jdbc/postgres] is not bound in this Context. Unable to find [comp].

这是查找DataSource

的代码

JNDI查找代码

        Context ctx = new InitialContext();
        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/postgres");

我试图解决这个问题:

我怀疑由于泄漏或不当部署导致应用程序取消部署后,旧的上下文仍然存在。尝试使用ServletContextListener方法从onDestroy()的{​​{1}}正常关闭消息侦听器(jmsSomeMessageListener)没有帮助。

在调用stop()之前停止消息监听器也无济于事。我希望重新启动监听器会强制它使用新的上下文。

问题:

为什么tomcat:undeploy在重新部署后会变得陈旧?这只发生在侦听器上,而不发生在查找相同Context的其他代码上。我该怎么做才能解决问题? (我甚至打开为每个CI构建重新启动服务器以暂时解决问题)

1 个答案:

答案 0 :(得分:1)

我解决了类似的问题,我的数据库池保持打开状态,但在重新部署战争后我无法使用JNDI查找。我通过关闭连接并在取消部署战争时从JNDI取消绑定来解决了这个问题。

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyClass implements ServletContextListener
{
    private String db = "foo";

    @Override
    public void contextDestroyed(ServletContextEvent sce)
    {
        PGPoolingDataSource connectionPool = 
                (PGPoolingDataSource)new InitialContext().lookup(db);
        if(connectionPool != null)
        {
            connectionPool.close();
            new InitialContext().unbind(db);
        }
    }
}

您还需要在web.xml中添加一个块,将您的类注册为侦听器:

   <listener>
       <listener-class>com.foo.bar.MyClass</listener-class>
   </listener>