我正在为我处理一个新的场景,我认为这可能对某些人来说很常见:)..
根据要求,我需要构建一个用户体验,就像Web服务调用的同步在线事务一样,它实际上使用异步JMS-MQ Bridge将调用委托给IBM MQ Series。
客户端调用Web服务,而且他的消息应该发布在App服务器上的JMS队列中,该队列将被传递到WebSphere MQ,并且在处理之后,响应将被传递回FIXED JMS队列端点中的App服务器。
该要求处理此事务,如果WebSphere MQ未在规定的时间内传递响应,则需要超时,而Web服务应向客户端发送超时信号并忽略此事务。 / p>
问题的草图如下。
我需要阻止Web服务上的请求,直到响应到达或超时。
我正在寻找一些开放的图书馆来帮助我完成这项任务。 或者唯一的解决方案是阻止线程并保持池的响应? 也许我可以用一个监听器实现一些块,以便在响应到来时得到通知?
现在进行一些讨论对我来说非常有帮助,试图澄清我的想法。 有什么建议吗?
我有一个草图,希望能帮助清理图片;)
答案 0 :(得分:2)
嘿,谢谢您发布自己的解决方案!
是的,在这种情况下,带超时的receive()是最优雅的方式。
请注意因超时而未读取的消息会发生什么。如果您的客户端再次加入相同的队列,他可能会收到陈旧的消息。
确保及时删除超时的消息(如果没有其他原因,则不用未处理的消息填充队列)。
您可以通过代码(设置消息生成器的生存时间)或Websphere MQ服务器(使用自动使消息过期的队列)轻松完成此操作。
如果您不能/不想修改代码的MQ端,后者会更容易。这就是我要做的事情:))
答案 1 :(得分:1)
到目前为止我编写的代码符合要求。它是一个带有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();
}
}
}
[]中