传递JMS消息时刷新Spring上下文

时间:2011-11-14 15:20:19

标签: spring spring-mvc spring-integration

我想在系统收到JMS消息时刷新我的应用程序上下文。为了做到这一点,我设置了Spring Integration jms:message-driven-channel-adapter,它将消息转发给实现ApplicationContextAware的服务激活器。此激活器(ConfigurationReloader类)调用ConfigurableApplicationContext#refresh()方法。

以下是示例代码段:

 <jms:message-driven-channel-adapter id="jmsDriverConfigurationAdapter"
    destination="configurationApplyQueue" channel="jmsConfigurationInboundChannel" />

 <channel id="jmsConfigurationInboundChannel"/>

 <service-activator input-channel="jmsConfigurationInboundChannel" ref="configurationReloader" method="refresh"/>

我的激活者:

public final class ConfigurationReloader implements ApplicationContextAware {
        private ConfigurableApplicationContext applicationContext;

        public void refresh() {
           this.applicationContext.refresh();
        }

        @Override
        public void setApplicationContext(
                final ApplicationContext applicationContext) throws BeansException {
            if (applicationContext instanceof ConfigurableApplicationContext) {
                this.applicationContext =
                    (ConfigurableApplicationContext) applicationContext;
            }
        }
    }

如果传递此类消息,则上下文启动关闭操作但仍停留在DefaultMessageListenerContainer bean shutdown:

2011-11-14 15:42:52,980 [org.springframework.jms.listener.DefaultMessageLis tenerContainer#0-1] DEBUG org.springframework.jms.listener.DefaultMessageLis tenerContainer - Shutting down JMS listener container
2011-11-14 15:42:52,980 [org.springframework.jms.listener.DefaultMessageLis tenerContainer#0-1] DEBUG org.springframework.jms.listener.DefaultMessageLis tenerContainer - Waiting for shutdown of message listener invokers
2011-11-14 15:42:55,104 [org.springframework.jms.listener.DefaultMessageLis tenerContainer#0-1] DEBUG org.springframework.jms.listener.DefaultMessageLis tenerContainer - Still waiting for shutdown of 1 message listener invokers

通过JMS调用此操作对我来说至关重要,因为新的配置参数随消息一起提供。 它是基于最新SpringCore和Spring Integration的标准Spring MVC应用程序,前端带有DispatcherServlet。此外,我确信它是JMS相关的问题,因为通过控制器调用ConfigurationLoader工作正常。

在我调试之后,它停留在DefaultMessageListenerContainer#538行调用(lifecycleMonitor上的wait()方法)之后:

/**
 * Destroy the registered JMS Sessions and associated MessageConsumers.
 */
protected void doShutdown() throws JMSException {
    logger.debug("Waiting for shutdown of message listener invokers");
    try {
        synchronized (this.lifecycleMonitor) {
            while (this.activeInvokerCount > 0) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Still waiting for shutdown of " + this.activeInvokerCount +
                            " message listener invokers");
                }
                this.lifecycleMonitor.wait();   // <--- line 538
            }
        }
    }
    catch (InterruptedException ex) {
        // Re-interrupt current thread, to allow other threads to react.
        Thread.currentThread().interrupt();
    }
}

...没有人在监视器上调用notify / notifyAll所以也许是某种bug?

感谢您的任何提示!

1 个答案:

答案 0 :(得分:2)

您能解释一下为什么需要这样复杂的架构?收到JMS消息时重新加载应用程序上下文?听起来很疯狂(或者可能是巧妙的?)

尽管如此,我并非100%确定,但您提供的信息非常明确:您正在尝试在使用JMS消息时关闭应用程序上下文。但由于消费者是Spring管理的,因此上下文不会被破坏,因为它等待所有bean完成 - 包括Spring Integration消息使用者所需的ConfigurationReloader。并且ConfigurationReloader无法完成,因为它等待上传被销毁(refresh()正在阻止)。

简单地说 - 你已经引入了循环依赖和死锁。

解决方案很简单 - 延迟上下文刷新,以便在JMS消息消耗之后发生。最简单的方法是:

public void refresh() {
    Thread destroyThread = new Thread() {
        @Override
        public void run() {
            this.applicationContext.refresh();
        }
    };
    destroyThread.start();
}

不漂亮,但我几乎可以肯定这会奏效。