【问题标题】:This method is not applicable inside the app server when trying to set a listener for a JMS queue尝试为 JMS 队列设置侦听器时,此方法不适用于应用服务器内部
【发布时间】:2015-08-22 15:27:51
【问题描述】:

我正在尝试 JMS 2.0,因此我可以决定它是否值得在我的项目中应用。我可以成功创建一个发送/接收应用程序。

现在我想让侦听器在消息在队列中可用时立即接收消息(我的最终目标是让不同的侦听器到同一个队列,每个侦听器都有不同的消息选择器。

目前我有这个课程:

package learning.jms;


import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;

@Named(value="senderBean")
@SessionScoped
public class SenderBean implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Resource(mappedName="queues/myQueue")
    private transient Queue myQueue;

    @Inject
    @JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
    private transient JMSContext context;

    private String messageText;

    private int nextType = 3;
    private transient JMSConsumer consumer;
    private transient JMSConsumer consumer2;
    private transient JMSConsumer consumer3;

    public SenderBean() {
    }

    @PostConstruct
    public void setUp(){

    }

    public String getMessageText() {
        return messageText;
    }

    public void setMessageText(String messageText) {
        this.messageText = messageText;
    }

    public void sendJMSMessageToMyQueue() {
        try {

            consumer = context.createConsumer(myQueue, "type=1");
            consumer.setMessageListener(new ListenerTypeOne());

//          consumer2 = context.createConsumer(myQueue, "type=2");
//          consumer2.setMessageListener(new ListenerTypeTwo());
//          
//          consumer3 = context.createConsumer(myQueue, "type=3");
//          consumer3.setMessageListener(new ListenerTypeThree());

           String text = "Message from producer: " + messageText;
           Message m1 = context.createTextMessage(text);
           m1.setIntProperty("type", nextType);

           System.out.println("producer sending msg type " + nextType + "value: " + text);

           nextType = (nextType++%3)+1;


           context.createProducer().send(myQueue, m1);

           FacesMessage facesMessage =
                   new FacesMessage("Sent message: " + text);
           FacesContext.getCurrentInstance().addMessage(null, facesMessage);
       } catch (JMSRuntimeException | JMSException t) {
            System.out.println(t.toString());
       }
   }

    private class ListenerTypeOne implements MessageListener{

        @Override
        public void onMessage(Message msg) {
            try {
                System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
            } catch (JMSException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private class ListenerTypeTwo implements MessageListener{

        @Override
        public void onMessage(Message msg) {
            try {
                System.out.println("Msg received by typeTwo:" + msg.getBody(String.class));
            } catch (JMSException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

    private class ListenerTypeThree implements MessageListener{

        @Override
        public void onMessage(Message msg) {
            try {
                System.out.println("Msg received by typeThree:" + msg.getBody(String.class));
            } catch (JMSException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }
}

我注释掉了两个消费者,这样我就可以专注于制作一个作品。 我在 setMessageListener 行上不断收到以下异常:

 javax.jms.IllegalStateException: This method is not applicable inside the application server. See the J2EE spec, e.g. J2EE1.4 Section 6.6
    at org.hornetq.ra.HornetQRASession.checkStrict(HornetQRASession.java:1647)
    at org.hornetq.ra.HornetQRAMessageConsumer.setMessageListener(HornetQRAMessageConsumer.java:124)
    at org.hornetq.jms.client.HornetQJMSConsumer.setMessageListener(HornetQJMSConsumer.java:68)

我不知道是什么原因造成的,我的搜索也没有给我任何额外的信息。 我想这可能与一个组件不应超过一个活动会话这一事实有关。在这种情况下,如何创建多个监听器来监听队列?

(如果重要:我使用的是 Wildfly 8)

编辑 我已将侦听器创建提取到单独的 bean 中,但仍然出现相同的错误:

package learning.jms;


import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;


@ApplicationScoped
public class ListenerOne {
    @Inject
    @JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
    private JMSContext context;

    @Resource(mappedName="queues/myQueue")
    private Queue myQueue;


    private JMSConsumer consumer;

    public void setUp() {
        consumer = context.createConsumer(myQueue, "type=1");
        consumer.setMessageListener(new ListenerTypeOne());


        System.out.println("working");
    }

    private class ListenerTypeOne implements MessageListener{

        @Override
        public void onMessage(Message msg) {
            try {
                System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
            } catch (JMSException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

【问题讨论】:

  • 快速谷歌搜索发现了这个:developer.jboss.org/thread/197366?tstart=0你确定你没有被这个咬吗?
  • 我已经发送了这篇文章。我认为这与 JMS 1.1 有关,对吧?我正在使用 JMS2.0
  • 我不确定这是否是解决方案不适用的唯一原因。因为,Hornet API 可能没有改变,而且这些 API 大多是向后兼容的。您可以查看 Hornet 的文档以了解此错误吗?
  • 所以,你是对的。我搜索了 MDB,这正是我所需要的。因为您让我找到解决方案,所以我想将您的回复标记为答案。如果您将其作为答案发布并使用详细信息进行编辑,这样可以吗?所以我们发布了答案并且您得到积分?
  • :) 你真是太慷慨了。但您可以回答问题并将其标记为已回答。我只是引导,点不一定就是点。 :)

标签: java jms


【解决方案1】:

所以,寻找 MDB 解决了这个问题。 我从我试图创建的消费者的任何痕迹中清除了 senderBean 类:

package learning.jms;


import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.Queue;

@Named(value="senderBean")
@SessionScoped
public class SenderBean implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Resource(mappedName="queues/myQueue")
    private transient Queue myQueue;

    @Inject
    @JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
    private transient JMSContext context;

    private String messageText;

    private int nextType;

    public SenderBean() {
        // TODO Auto-generated constructor stub
    }

    @PostConstruct
    public void init(){
        nextType=2;
    }

    public String getMessageText() {
        return messageText;
    }

    public void setMessageText(String messageText) {
        this.messageText = messageText;
    }

    public void sendJMSMessageToMyQueue() {
        try {
           String text = "Message from producer: " + messageText;
           Message m1 = context.createTextMessage(text);
           m1.setIntProperty("type", nextType);

           nextType = (nextType++%3)+1;

           context.createProducer().send(myQueue, m1);

           FacesMessage facesMessage =
                   new FacesMessage("Sent message: " + text);
           FacesContext.getCurrentInstance().addMessage(null, facesMessage);
       } catch (JMSRuntimeException | JMSException t) {
            System.out.println(t.toString());
       }
   }
}

(注意它只是会话范围,所以我可以遍历消息类型”)

并创建了 3 个 MDB

package learning.jms;


import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;



@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationLookup",
            propertyValue = "queues/myQueue"),
        @ActivationConfigProperty(propertyName = "destinationType",
            propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=1")
    })
public class ListenerOne implements MessageListener {

    @Override
    public void onMessage(Message msg) {
        try {
            System.out.println("Msg received by typeOne: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

两个:

package learning.jms;


import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;



@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationLookup",
            propertyValue = "queues/myQueue"),
        @ActivationConfigProperty(propertyName = "destinationType",
            propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=2")
    })
public class ListenerTwo implements MessageListener {

    @Override
    public void onMessage(Message msg) {
        try {
            System.out.println("Msg received by typeTwo: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

三个:

package learning.jms;


import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;



@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationLookup",
            propertyValue = "queues/myQueue"),
        @ActivationConfigProperty(propertyName = "destinationType",
            propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=3")
    })
public class ListenerThree implements MessageListener {

    @Override
    public void onMessage(Message msg) {
        try {
            System.out.println("Msg received by typeThree: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

现在它们会自动侦听队列中与其选择器匹配的消息。

感谢@prabugp 的帮助:)

【讨论】:

    【解决方案2】:

    上述错误可能是因为您试图在同一个容器内使用客户端并从基于 JCA 的连接工厂获取连接工厂。

    案例1:如果客户端是远程jms服务器,那么推荐使用jms/RemoteConnectionFactory,以上问题不会重现。

    案例 2:如果客户端驻留在同一个容器中,则首选来自基于 JCA 的连接工厂 java/JmsXA 的连接。由于 JEE 7 规范在第 6:7 节中有一个限制,即 JEE 服务器不允许 EJB/Web 应用程序拥有多个活动会话,即您不能拥有旧版 JMS 应用程序。

    例如:在方法中:

    public void startConnection() {
            try {
                TopicConnectionFactory connectionFactory = (TopicConnectionFactory)    getConnectionFactory();
                topicConnection = connectionFactory.createTopicConnection();
                topicSession = topicConnection.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);           
                subscriber = topicSession.createSubscriber(messageTopic, selector, true);
                MessageListener messageListener = new MessageListener(this);
                // Code to set message listener.
                subscriber.setMessageListener(messageListener);
                topicConnection.start();
            } catch (Exception e) {
                LOG.error(e, e);
                closeConnection();
                throw new RuntimeException(e);
            }
        }
    

    如果上面代码的连接工厂来自 @Resource(mappedName = "java:jboss/exported/jms/RemoteConnectionFactory")

    那么上面的代码就可以工作了。但是如果我们将连接工厂更改为 //@Resource(mappedName = "java:/JmsXA")

    然后会抛出上述错误。

    因此,如果您的客户端在同一个容器中,则应使用 MDB。由于容器希望控制连接对象以支持两阶段提交协议。

    【讨论】:

      【解决方案3】:

      我的观点是,在 JSF bean 中不能有消息监听器,因为 bean 生命周期是由 Web 容器控制的。

      MDB 是唯一由消息驱动的组件,但 JSF MB 与 EJB 或 servlet 一样,不能侦听消息,它们“活”在请求的上下文中,实例由以下人员创建、激活、钝化或销毁容器。

      但是,您可以在请求的上下文中使用 receive(),并在客户端设置一些自动刷新,以实现服务器端驱动的流程。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-12-05
        • 2018-03-18
        • 1970-01-01
        • 2013-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多