无状态 bean 中的 JMS 队列资源

JMS queue resource in a stateless bean

我有以下无状态 bean

@Local(MessageDispatcher.class)
@Stateless
public class MessageDispatcherImpl implements MessageDispatcher {

    private static final Logger log = LoggerFactory.getLogger(MessageDispatcher.class);

    @Resource(name = "java:/jms/queue/outgoing")
    private Queue outgoingQueue;

    @Inject
    private JMSContext jms;

    private JMSProducer jmsProducer;

    @PostConstruct
    public void postConstruct() {
        log.info("Initializing message dispatcher");
        jmsProducer = jms.createProducer();
    }

    @Override
    public void publishToJms(Message message) throws JMSException {
        jmsProducer.send(outgoingQueue, message);       
    }

}

它只是负责为其他 bean 提供将消息发布到特定队列的能力。它不需要跨调用保持其状态,也不需要与客户端保持 "conversational state",因此选择无状态 bean。

但是,当调用方法 publishToJms 时,我收到以下运行时错误:

ERROR [org.jboss.as.ejb3.invocation] (MQTT Call: xyz) WFLYEJB0034: EJB Invocation failed on component MessageDispatcherImpl for method public default void com.xyz.MessageDispatcherImpl.publishToJms(java.lang.String,byte[]) throws javax.jms.JMSException: javax.ejb.EJBException: javax.jms.IllegalStateRuntimeException: The session is closed
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleExceptionInOurTx(CMTTxInterceptor.java:187)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:277)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:327)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:239)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:43)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:100)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory.processInvocation(ShutDownInterceptorFactory.java:64)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:66)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:54)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:64)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:356)
    at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:636)
    at org.jboss.invocation.AccessCheckingInterceptor.processInvocation(AccessCheckingInterceptor.java:61)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:356)
    at org.jboss.invocation.PrivilegedWithCombinerInterceptor.processInvocation(PrivilegedWithCombinerInterceptor.java:80)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:195)
    at org.jboss.as.ee.component.ViewDescription.processInvocation(ViewDescription.java:185)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:73)
    at com.togather.messaging.mqtt.MqttToJmsBridge$$$view8.publishToJms(Unknown Source)
    at com.togather.messaging.mqtt.impl.TogatherMqttClientImpl.read(TogatherMqttClientImpl.java:150)
    at com.togather.messaging.mqtt.impl.TogatherMqttClientImpl.messageArrived(TogatherMqttClientImpl.java:188)
    at org.eclipse.paho.client.mqttv3.internal.CommsCallback.handleMessage(CommsCallback.java:354)
    at org.eclipse.paho.client.mqttv3.internal.CommsCallback.run(CommsCallback.java:162)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.jms.IllegalStateRuntimeException: The session is closed
    at org.apache.activemq.artemis.jms.client.JmsExceptionUtils.convertToRuntimeException(JmsExceptionUtils.java:59)
    at org.apache.activemq.artemis.jms.client.ActiveMQJMSContext.createObjectMessage(ActiveMQJMSContext.java:302)
    at org.apache.activemq.artemis.jms.client.ActiveMQJMSProducer.send(ActiveMQJMSProducer.java:196)
    at com.togather.messaging.mqtt.impl.MqttToJmsBridgeImpl.publishToJms(MqttToJmsBridgeImpl.java:61)
    at com.togather.messaging.mqtt.MqttToJmsBridge.publishToJms(MqttToJmsBridge.java:37)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.jboss.as.ee.component.ManagedReferenceMethodInterceptor.processInvocation(ManagedReferenceMethodInterceptor.java:52)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:437)
    at org.jboss.as.weld.ejb.Jsr299BindingsInterceptor.doMethodInterception(Jsr299BindingsInterceptor.java:82)
    at org.jboss.as.weld.ejb.Jsr299BindingsInterceptor.processInvocation(Jsr299BindingsInterceptor.java:93)
    at org.jboss.as.ee.component.interceptors.UserInterceptorFactory.processInvocation(UserInterceptorFactory.java:63)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.invocationmetrics.ExecutionTimeInterceptor.processInvocation(ExecutionTimeInterceptor.java:43)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.jpa.interceptor.SBInvocationInterceptor.processInvocation(SBInvocationInterceptor.java:47)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:437)
    at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:73)
    at org.jboss.as.weld.ejb.EjbRequestScopeActivationInterceptor.processInvocation(EjbRequestScopeActivationInterceptor.java:83)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.InitialInterceptor.processInvocation(InitialInterceptor.java:21)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(ComponentDispatcherInterceptor.java:52)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.component.pool.PooledInstanceInterceptor.processInvocation(PooledInstanceInterceptor.java:51)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:275)
    ... 38 more
Caused by: javax.jms.IllegalStateException: The session is closed
    at org.apache.activemq.artemis.ra.ActiveMQRASession.getSessionInternal(ActiveMQRASession.java:1575)
    at org.apache.activemq.artemis.ra.ActiveMQRASession.createObjectMessage(ActiveMQRASession.java:234)
    at org.apache.activemq.artemis.jms.client.ActiveMQJMSContext.createObjectMessage(ActiveMQJMSContext.java:299)
    ... 70 more

我找到了解决此错误的两种方法:

首先是做beanstateful.

第二个是稍微改变方法 publishToJms 每次都实例化 jmsProducer,就像那样

public void publishToJms(MqttBridgeMessage bridgeMessage) throws JMSException {
    JMSProducer jmsProducer = jms.createProducer();
    jmsProducer.send(mqttIncomingMessageBridgeQueue, bridgeMessage);
}

我的问题是:这种行为是预期的吗?如果是这样,任何人都可以向我解释错误发生的原因,或者指向网络上的资源吗?

在未指定的事务上下文中用 @PostConstruct 运行 注释的方法。现在,JMS 规范中有这样的描述:

4.4.1 Closing a Session Since a provider may allocate some resources on behalf of a session outside the JVM, clients should close them when they are not needed. Relying on garbage collection to eventually reclaim these resources may not be timely enough. The same is true for the MessageProducers and MessageConsumers created by a session.

因此,在 post 构造之后关闭会话是非常合适的,因为 @PostConstruct 方法中的事务可能会在此类方法执行后结束,并且正在使用的类型事务管理是容器的。