消息驱动通道适配器在应用程序上下文启动后丢弃第一条消息,除非调用send并延迟

时间:2017-01-15 23:20:09

标签: activemq spring-integration spring-jms

我对我的Spring Integration配置进行了集成测试,该测试使用来自具有持久订阅的JMS主题的消息。为了测试,我使用的是ActiveMQ而不是Tibco EMS。 我遇到的问题是我必须在测试方法开始时使用sleep调用将第一条消息发送到端点。否则消息将被删除。 如果我删除持久订阅和选择器的设置,则可以立即发送第一条消息而不会有任何延迟。 我想摆脱睡眠,这是不可靠的。有没有办法在发送消息之前检查端点是否已完全设置? 以下是配置。

感谢您的帮助!

    <int-jms:message-driven-channel-adapter
        id="myConsumer" connection-factory="myCachedConnectionFactory"
        destination="myTopic" channel="myChannel" error-channel="errorChannel"
        pub-sub-domain="true" subscription-durable="true"
        durable-subscription-name="testDurable"
        selector="..."
        transaction-manager="emsTransactionManager" auto-startup="false"/>

1 个答案:

答案 0 :(得分:1)

如果您使用干净的嵌入式activemq进行测试,则在建立订阅之前,订阅的持久性无关紧要。所以你别无选择,只能等到发生这种情况。

您可以通过发送一系列启动消息来避免睡眠,并且仅在收到最后一个消息时启动真实测试。

修改

我忘了isRegisteredWithDestination()上有DefaultMessageListenerContainer方法。

...的Javadocs

/**
 * Return whether at least one consumer has entered a fixed registration with the
 * target destination. This is particularly interesting for the pub-sub case where
 * it might be important to have an actual consumer registered that is guaranteed
 * not to miss any messages that are just about to be published.
 * <p>This method may be polled after a {@link #start()} call, until asynchronous
 * registration of consumers has happened which is when the method will start returning
 * {@code true} &ndash; provided that the listener container ever actually establishes
 * a fixed registration. It will then keep returning {@code true} until shutdown,
 * since the container will hold on to at least one consumer registration thereafter.
 * <p>Note that a listener container is not bound to having a fixed registration in
 * the first place. It may also keep recreating consumers for every invoker execution.
 * This particularly depends on the {@link #setCacheLevel cache level} setting:
 * only {@link #CACHE_CONSUMER} will lead to a fixed registration.
 */

我们在some channel tests中使用它,我们使用反射获取容器,然后轮询该方法,直到我们订阅该主题。

/**
 * Blocks until the listener container has subscribed; if the container does not support
 * this test, or the caching mode is incompatible, true is returned. Otherwise blocks
 * until timeout milliseconds have passed, or the consumer has registered.
 * @see DefaultMessageListenerContainer#isRegisteredWithDestination()
 * @param timeout Timeout in milliseconds.
 * @return True if a subscriber has connected or the container/attributes does not support
 * the test. False if a valid container does not have a registered consumer within
 * timeout milliseconds.
 */
private static boolean waitUntilRegisteredWithDestination(SubscribableJmsChannel channel, long timeout) {
    AbstractMessageListenerContainer container =
            (AbstractMessageListenerContainer) new DirectFieldAccessor(channel).getPropertyValue("container");
    if (container instanceof DefaultMessageListenerContainer) {
        DefaultMessageListenerContainer listenerContainer =
            (DefaultMessageListenerContainer) container;
        if (listenerContainer.getCacheLevel() != DefaultMessageListenerContainer.CACHE_CONSUMER) {
            return true;
        }
        while (timeout > 0) {
            if (listenerContainer.isRegisteredWithDestination()) {
                return true;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) { }
            timeout -= 100;
        }
        return false;
    }
    return true;
}