如何使用请求 - 回复模式在同步调用中将JMS包装到WebSphere MQ桥?

时间:2010-12-09 19:47:21

标签: asynchronous jms weblogic integration ibm-mq

我正在为我处理一个新的场景,我认为这可能对某些人来说很常见:)..

根据要求,我需要构建一个用户体验,就像Web服务调用的同步在线事务一样,它实际上使用异步JMS-MQ Bridge将调用委托给IBM MQ Series。

客户端调用Web服务,而且他的消息应该发布在App服务器上的JMS队列中,该队列将被传递到WebSphere MQ,并且在处理之后,响应将被传递回FIXED JMS队列端点中的App服务器。

该要求处理此事务,如果WebSphere MQ未在规定的时间内传递响应,则需要超时,而Web服务应向客户端发送超时信号并忽略此事务。 / p>

问题的草图如下。

我需要阻止Web服务上的请求,直到响应到达或超时。

我正在寻找一些开放的图书馆来帮助我完成这项任务。 或者唯一的解决方案是阻止线程并保持池的响应? 也许我可以用一个监听器实现一些块,以便在响应到来时得到通知?

现在进行一些讨论对我来说非常有帮助,试图澄清我的想法。 有什么建议吗?

我有一个草图,希望能帮助清理图片;)

alt text

2 个答案:

答案 0 :(得分:2)

嘿,谢谢您发布自己的解决方案!

是的,在这种情况下,带超时的receive()是最优雅的方式。

请注意因超时而未读取的消息会发生什么。如果您的客户端再次加入相同的队列,他可能会收到陈旧的消息。

确保及时删除超时的消息(如果没有其他原因,则不用未处理的消息填充队列)。

您可以通过代码(设置消息生成器的生存时间)或Websphere MQ服务器(使用自动使消息过期的队列)轻松完成此操作。

如果您不能/不想修改代码的MQ端,后者会更容易。这就是我要做的事情:))

答案 1 :(得分:1)

经过几天的编码,我得到了一个解决方案。我正在使用带有JAX-WS注释和标准JMS的标准EJB3。

到目前为止我编写的代码符合要求。它是一个带有bean托管事务(BMT)的无状态会话Bean,因为使用标准容器管理事务(CMT)会导致某种挂起,我相信因为我试图将两个JMS交互放在同一个事务中,因为它们在同样的方法通知我必须为每次与JMS队列的交互启动和完成事务。我正在使用weblogic来解决这个问题。我还编写了一个MDB,它基本上消耗来自队列端点jms / Pergunta的消息,并在jms / Resposta队列上放置响应消息,我这样做是为了模拟这个问题的MQ端的预期行为。实际上在一个真实的场景中,我们可能在大型机上有一些COBOL应用程序,甚至是处理消息并将响应放在响应队列上的其他java应用程序。

如果有人需要尝试这个代码,基本上你需要的是拥有一个容器J2EE5并使用jndi名称配置2个队列:jms / Pergunta和jms / Resposta。

EJB / Webservice代码:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
@WebService(name="DJOWebService")
public class DJOSessionBeanWS implements DJOSessionBeanWSLocal {

    Logger log = Logger.getLogger(DJOSessionBeanWS.class.getName());

    @Resource
    SessionContext ejbContext;

    // Defines the JMS connection factory.
    public final static String JMS_FACTORY = "weblogic.jms.ConnectionFactory";

    // Defines request queue
    public final static String QUEUE_PERG = "jms/Pergunta";

    // Defines response queue
    public final static String QUEUE_RESP = "jms/Resposta";


    Context ctx;
    QueueConnectionFactory qconFactory;

    /**
     * Default constructor. 
     */
    public DJOSessionBeanWS() {
        log.info("Construtor DJOSessionBeanWS");
    }

    @WebMethod(operationName = "processaMensagem")
    public String processaMensagem(String mensagemEntrada, String idUnica)
    {
        //gets UserTransaction reference as this is a BMT EJB.
        UserTransaction ut = ejbContext.getUserTransaction();
        try {

            ctx = new InitialContext();
            //get the factory before any transaction it is a weblogic resource.
            qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
            log.info("Got QueueConnectionFactory");
            ut.begin();
            QueueConnection qcon = qconFactory.createQueueConnection();
            QueueSession qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            Queue qs = (Queue) (new InitialContext().lookup("jms/Pergunta"));
            TextMessage message = qsession.createTextMessage("this is a request message");
            message.setJMSCorrelationID(idUnica);
            qsession.createSender(qs).send(message);
            ut.commit();
            qcon.close();
            //had to finish and start a new transaction, I decided also get new references for all JMS related objects, not sure if this is REALLY required
            ut.begin();
            QueueConnection queuecon = qconFactory.createQueueConnection();
            Queue qreceive = (Queue) (new InitialContext().lookup("jms/Resposta"));
            QueueSession queuesession = queuecon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            String messageSelector = "JMSCorrelationID = '" + idUnica + "'";
            //creates que receiver and sets a message selector to get only related message from the response queue.
                    QueueReceiver qr = queuesession.createReceiver(qreceive, messageSelector);
            queuecon.start();
            //sets the timeout to keep waiting for the response...
            TextMessage tresposta = (TextMessage) qr.receive(10000);
            if(tresposta != null)
            {
                ut.commit();
                queuecon.close();
                return(tresposta.toString());
            }
            else{
                //commints anyway.. does not have a response though 
                ut.commit();
                queuecon.close();
                log.info("null reply, returned by timeout..");
                return "Got no reponse message.";
            }



        } catch (Exception e) {
            log.severe("Unexpected error occurred ==>> " + e.getMessage());
            e.printStackTrace();
            try {
                ut.commit();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return "Error committing transaction after some other error executing ==> " + e.getMessage();
        } 

    }
}   

这是MDB的代码,它可以解决这个问题的MQ方面。我在测试期间有一个Thread.sleep片段来模拟和测试客户端的超时以验证解决方案,但在此版本中不存在。

/**
 * Mock to get message from request queue and publish a new one on the response queue.
 */
@MessageDriven(
        activationConfig = { @ActivationConfigProperty(
                propertyName = "destinationType", propertyValue = "javax.jms.Queue"
        ) }, 
        mappedName = "jms/Pergunta")
public class ConsomePerguntaPublicaRespostaMDB implements MessageListener {

    Logger log = Logger.getLogger(ConsomePerguntaPublicaRespostaMDB.class.getName());

    // Defines the JMS connection factory.
    public final static String JMS_FACTORY = "weblogic.jms.ConnectionFactory";

    // Define Queue de resposta
    public final static String QUEUE_RESP = "jms/Resposta";


    Context ctx;
    QueueConnectionFactory qconFactory;



    /**
     * Default constructor. 
     */
    public ConsomePerguntaPublicaRespostaMDB() {
        log.info("Executou construtor ConsomePerguntaPublicaRespostaMDB");
        try {
            ctx = new InitialContext();
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    /**
     * @see MessageListener#onMessage(Message)
     */
    public void onMessage(Message message) {
        log.info("Recuperou mensagem da fila jms/FilaPergunta, executando ConsomePerguntaPublicaRespostaMDB.onMessage");
        TextMessage tm = (TextMessage) message;

        try {
            log.info("Mensagem recebida no onMessage ==>> " + tm.getText());

            //pega id da mensagem na fila de pergunta para setar corretamente na fila de resposta.
             String idMensagem = tm.getJMSCorrelationID();
             log.info("Id de mensagem que sera usada na resposta ==>> " + idMensagem);

            qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
            log.info("Inicializou contexto jndi e deu lookup na QueueConnectionFactory do weblogic com sucesso. Enviando mensagem");
            QueueConnection qcon = qconFactory.createQueueConnection();
            QueueSession qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            Queue queue = (Queue) (ctx.lookup("jms/Resposta"));
            TextMessage tmessage = qsession.createTextMessage("Mensagem jms para postar na fila de resposta...");
            tmessage.setJMSCorrelationID(idMensagem);
            qsession.createSender(queue).send(tmessage);
        } catch (JMSException e) {
            log.severe("Erro no onMessage ==>> " + e.getMessage());
            e.printStackTrace();
        }  catch (NamingException e) {
            log.severe("Erro no lookup ==>> " + e.getMessage());
            e.printStackTrace();
        }

    }

}

[]中