I am trying to create a synchronous request using JMS on JBoss
Code for MDB is:
#Resource(mappedName = "java:/ConnectionFactory")
private ConnectionFactory connectionFactory;
#Override
public void onMessage(Message message) {
logger.info("Received message for client call");
if (message instanceof ObjectMessage) {
Connection con = null;
try {
con = connectionFactory.createConnection();
con.start();
Requests requests = (Requests) ((ObjectMessage) message)
.getObject();
String response = getClient().get(getRequest(requests));
con = connectionFactory.createConnection();
Session ses = con.createSession(true, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = ses.createProducer(message
.getJMSReplyTo());
TextMessage replyMsg = ses.createTextMessage();
replyMsg.setJMSCorrelationID(message.getJMSCorrelationID());
replyMsg.setText(response);
logger.info("Sending reply to client call : " + response );
producer.send(replyMsg);
} catch (JMSException e) {
logger.severe(e.getMessage());
} finally {
if (con != null) {
try {
con.close();
} catch (Exception e2) {
logger.severe(e2.getMessage());
}
}
}
}
}
Code for client is:
#Resource(mappedName = "java:/ConnectionFactory")
private QueueConnectionFactory queueConnectionFactory;
#Resource(mappedName = "java:/queue/request")
private Queue requestQueue;
#Override
public Responses getResponses(Requests requests) {
QueueConnection connection = null;
try {
connection = queueConnectionFactory.createQueueConnection();
connection.start();
QueueSession session = connection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session
.createProducer(requestQueue);
ObjectMessage message = session.createObjectMessage();
message.setObject(requests);
TemporaryQueue temp = session.createTemporaryQueue();
MessageConsumer consumer = session.createConsumer(temp);
message.setJMSReplyTo(temp);
messageProducer.send(message);
Message response = consumer.receive();
if (response instanceof TextMessage) {
logger.info("Received response");
return new Responses(null, ((TextMessage) response).getText());
}
} catch (JMSException e) {
logger.severe(e.getMessage());
} finally {
if (connection != null) {
try {
connection.close();
} catch (Exception e2) {
logger.severe(e2.getMessage());
}
}
}
return null;
}
The message is received fine on the queue, the response message is created and the MessageProducer sends the response without issue, with no errors. However the consumer just sits and waits indefinitely. I have also tried creating a separate reply queue rather then using a temporary queue and the result is the same.
I am guessing that I am missing something basic with this set up but I cannot for the life of me see anything I am doing wrong.
There is no other code, the 2 things I have read on this that can cause problems is that the connection.start() isn't called or the repsonses are going to some other different receiver, which isn't happening here (as far as I know - there are no other messaging parts to the code outside of these classes yet)
So I guess my question is, should the above code work or am I missing some fundamental understanding of the JMS flow?
So..I persevered and I got it to work.
The answer is that when I create the session, the transacted attribute in both the client and the MDB had to be set to false:
Session ses = con.createSession(true, Session.AUTO_ACKNOWLEDGE);
had to be changed to:
Session ses = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
for both client and server.
I know why now! I am effectively doing the below which is taken from the Oracle JMS documentation!
If you try to use a request/reply mechanism, whereby you send a message and then try to receive a reply to the sent message in the same transaction, the program will hang, because the send cannot take place until the transaction is committed. The following code fragment illustrates the problem:
// Don’t do this!
outMsg.setJMSReplyTo(replyQueue);
producer.send(outQueue, outMsg);
consumer = session.createConsumer(replyQueue);
inMsg = consumer.receive();
session.commit();
Related
I am new, so my question is relatively easy, I guess.
I am using Websphere Application Server platform and default JMS provider to send and receive message from queue. This is how my app looks like:
Saytime is my main servlet which reroute my code to a .jsp file. The "Produce" button sends the app following code and generate the message written in box:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String queueName = "jms/helloqueue";
Context jndiContext = null;
QueueConnectionFactory queueConnectionFcatory = null;
QueueConnection queueConnection = null;
QueueSession queueSession = null;
QueueSender queueSender = null;
Queue queue = null;
TextMessage textMessage = null;
response.setContentType("text/html");
request.setCharacterEncoding("UTF-8"); // To information the that you may use Unicode characters
response.setCharacterEncoding("UTF-8");
String txt = request.getParameter("text");
try {
Properties initialProperties = new Properties();
initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
initialProperties.put(InitialContext.PROVIDER_URL, "iiop://localhost:2810");
jndiContext = new InitialContext(initialProperties);
} catch (NamingException e) {
e.printStackTrace();
System.exit(1);
}
try {
queueConnectionFcatory = (QueueConnectionFactory) jndiContext.lookup("jms/helloqcf");
queue = (Queue) jndiContext.lookup(queueName);
} catch (NamingException e) {
e.printStackTrace();
System.exit(1);
}
try {
queueConnection = queueConnectionFcatory.createQueueConnection();
queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queue);
textMessage = queueSession.createTextMessage();
textMessage.setText(txt);
queueSender.send(textMessage);
} catch (JMSException e) {
System.out.println("JMS Exception occured: "+ e.getMessage());
} finally {
if(queueConnection != null){
try{
Thread.sleep(6000);
queueConnection.close();
} catch(Exception e){}
}
}
RequestDispatcher rd = request.getRequestDispatcher("saytime");
rd.forward(request,response);
}
The "Receive" button sends my app to following servlet code and receives the message from queue:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String queueName = "jms/helloqueue";
Context jndiContext = null;
QueueConnectionFactory queueConnectionfactory = null;
QueueConnection queueConnection = null;
QueueSession queueSession = null;
QueueReceiver queueReceiver = null;
Queue queue = null;
String text = null;
try {
Properties initialProperties = new Properties();
initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
initialProperties.put(InitialContext.PROVIDER_URL,"iiop://localhost:2810");
jndiContext = new InitialContext(initialProperties);
} catch (NamingException e) {
System.out.println("JNDI exception occured: " + e.getMessage());
System.exit(1);
}
try {
queueConnectionfactory = (QueueConnectionFactory) jndiContext.lookup("jms/helloqcf");
queue = (Queue) jndiContext.lookup(queueName);
} catch (NamingException e) {
System.exit(1);
}
try {
queueConnection = queueConnectionfactory.createQueueConnection();
queueSession = queueConnection.createQueueSession(true,Session.AUTO_ACKNOWLEDGE);
queueReceiver = queueSession.createReceiver(queue);
//queueReceiver.setMessageListener(listener);
queueConnection.start();
text = queueReceiver.receive().toString();
} catch(JMSException e) {
System.out.println("Exception occured: "+ e.getMessage());
} finally {
if (queueConnection != null) {
try {
queueConnection.close();
} catch (JMSException e) {
}
}
}
if(text != null) {
request.setAttribute("message", text.toString());
}
RequestDispatcher rd = request.getRequestDispatcher("saytime");
rd.forward(request,response);
}
After that I print the message with this little code in my .jsp file:
<%
String getValues = (String) request.getAttribute("message");
%>
<%
if (getValues != null) {
out.println("<p>" + getValues + "</p>");
} else {
out.println("<p> There is no message </p>");
}
%>
The problem is this: I am able to take my produced message, but the button continues to receive the message till the count on JMSXDeliveryCount hit 5. Mostly JMSXDeliveryCount start with 1 and total I can receive the message 5 times. I want to receive it only once and then message to disappear.
Additionally, I want to know how I can print only my message. I print with additional details like you see in the picture. If it's possible, I don't want that.
I tried to limit redelivery number, but I am unable to come up with right code I guess. Also, I tried to use different acknowledgement mode but, it did not work either.
I got really confused with it, some help would be perfect. Thanks.
The problem is you're creating the consumer's session as transacted. See this line:
queueSession = queueConnection.createQueueSession(true,Session.AUTO_ACKNOWLEDGE);
The acknowledgement mode will be ignored since the session is transacted (i.e. you're passing true in the first parameter). This is noted in the documentation which states:
If transacted is set to true then the session will use a local transaction which may subsequently be committed or rolled back by calling the session's commit or rollback methods. The argument acknowledgeMode is ignored.
Therefore, you should either acknowledge the message and commit the session manually, e.g.:
Message message = queueReceiver.receive();
text = message.toString();
message.acknowledge();
queueSession.commit();
Or you should use a non-transacted session and allow the message to be auto-acknowledged according to the acknowledgement mode, e.g.:
queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Typically transacted sessions are only used when multiple operations (i.e. send and receive) need to be combined together atomically. Since you're only consuming a single message here I would recommend you just use a non-transacted session.
Also, you will eventually want to cache the javax.jms.Connection or perhaps use a connection pool rather than creating a connection, session, & producer/consumer for every message. This is an anti-pattern and should be avoided whenever possible.
I'm using ActiveMQ Artemis for a simple chat application. The message sending is working fine, and the consumer is consuming the message by executing onMessage(). But neither the onCompletion() nor the onException() methods of javax.jms.CompletionListener are not getting executed.
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://TJ12AA041-PC:61616");
Connection con = factory.createConnection();
con.start();
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("examp2");
MessageProducer producer = session.createProducer(null);
TextMessage txt = session.createTextMessage();
txt.setText("Hello Hello");
producer.send(topic, txt, new CompletionListener() {
#Override
public void onException(Message msg, Exception ex) {
System.out.println("Failed to send Messaeg : "+ex.getMessage());
}
#Override
public void onCompletion(Message msg) {
System.out.println("Message sent SuccessFully");
}
});
When I run the following code it seems that the message was sent to the queue but I can not see anythyng on the queue. There is no error, exception durig executing my code.
I use Weblogic server.
This is my code:
private InitialContext getInitialContext() throws NamingException {
Hashtable env = new Hashtable();
env.put(InitialContext.INITIAL_CONTEXT_FACTORY, contextFactory);
env.put(InitialContext.PROVIDER_URL, providerUrl);
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
return new InitialContext(env);
}
public ConnectionFactory getConnectionFactory(InitialContext context) throws NamingException {
return (ConnectionFactory) context.lookup(ConnectionParameter.JMS_CONNECTION_FACTORY_JNDI);
}
public void send() throws NamingException, JMSException {
InitialContext context = getInitialContext();
Destination destination = (Destination) context.lookup("jms/dpdr/mhcinterface/arnoldQueue");
try (Connection connection = getConnectionFactory(context).createConnection();){
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
MessageProducer sender = session.createProducer(destination);
Message message = session.createTextMessage("work order complete!");
sender.send(message);
session.commit();
session.close();
}
context.close();
System.out.println("-- end --");
}
Any idea what is wrong here please?
It looks like you forgot to call connection.start() before sending your message. You can do it like below:
MessageProducer sender = session.createProducer(destination);
connection.start();
Message message = session.createTextMessage("work order complete!");
I have a distributed queue on Weblogic. Messages are read from the queue using JMS onMessage() function. However the messages are not purged from the queue as long as the deployment is running. The message state string is always 'receive'. How do we ensure that the message is not picked up again in case a restart of the deployment is done?
#Override
public void onMessage(Message msg) {
try {
String msgText;
if (msg instanceof TextMessage) {
msgText = ((TextMessage) msg).getText();
} else {
msgText = msg.toString();
}
System.out.println("Message Received from Message_RESPONSE_QUEUE: " + msgText + " - " + count++);
// now send the message to queue2
InitialContext ic2 = getInitialContext2();
getMsgFromQueue qs = new getMsgFromQueue();
qs.init2(ic2, QUEUE2);
qs.send(msg, null);
} catch (JMSException jmse) {
} catch (NamingException ex) {
Logger.getLogger(getMsgFromQueue.class.getName()).log(Level.SEVERE, null, ex);
}
}
The message from the JMS queue does not get removed until JMS server receives an acknowledgement.
Here's some references that you may find useful -
http://docs.oracle.com/cd/E17904_01/web.1111/e15493/prog_details.htm#i1156227
http://docs.oracle.com/cd/E17904_01/web.1111/e15493/prog_details.htm#i1152248
http://docs.oracle.com/cd/E17904_01/web.1111/e15493/prog_details.htm#i1156227
I have a MDB running on JBoss 7.1, and a simple Java application as a client on another machine. The goal is the following:
the client sends a request (ObjectMessage) to the server
the server processes the request and sends back a response to the client (ObjectMessage again)
I thought to use a TemporaryQueue on the client to listen for the response (because I don't know how to do it asynchronously), and the JMSReplyTo Message's property to correctly reply back because I should support multiple independent clients.
This is the client:
public class MessagingService{
private static final String JBOSS_HOST = "localhost";
private static final int JBOSS_PORT = 5455;
private static Map connectionParams = new HashMap();
private Window window;
private Queue remoteQueue;
private TemporaryQueue localQueue;
private ConnectionFactory connectionFactory;
private Connection connection;
private Session session;
public MessagingService(Window myWindow){
this.window = myWindow;
MessagingService.connectionParams.put(TransportConstants.PORT_PROP_NAME, JBOSS_PORT);
MessagingService.connectionParams.put(TransportConstants.HOST_PROP_NAME, JBOSS_HOST);
TransportConfiguration transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName(), connectionParams);
this.connectionFactory = (ConnectionFactory) HornetQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, transportConfiguration);
}
public void sendRequest(ClientRequest request) {
try {
connection = connectionFactory.createConnection();
this.session = connection.createSession(false, QueueSession.AUTO_ACKNOWLEDGE);
this.remoteQueue = HornetQJMSClient.createQueue("testQueue");
this.localQueue = session.createTemporaryQueue();
MessageProducer producer = session.createProducer(remoteQueue);
MessageConsumer consumer = session.createConsumer(localQueue);
ObjectMessage message = session.createObjectMessage();
message.setObject(request);
message.setJMSReplyTo(localQueue);
producer.send(message);
ObjectMessage response = (ObjectMessage) consumer.receive();
ServerResponse serverResponse = (ServerResponse) response.getObject();
this.window.dispatchResponse(serverResponse);
this.session.close();
} catch (JMSException e) {
// TODO splittare e differenziare
e.printStackTrace();
}
}
Now I'm having troubles writing the server side, as I cannot figure out how to establish a Connection to a TemporaryQueue...
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
Destination replyDestination = message.getJMSReplyTo();
ObjectMessage objectMessage = (ObjectMessage) message;
ClientRequest request = (ClientRequest) objectMessage.getObject();
System.out.println("Queue: I received an ObjectMessage at " + new Date());
System.out.println("Client Request Details: ");
System.out.println(request.getDeparture());
System.out.println(request.getArrival());
System.out.println(request.getDate());
System.out.println("Replying...");
// no idea what to do here
Connection connection = ? ? ? ? ? ? ? ?
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer replyProducer = session.createProducer(replyDestination);
ServerResponse serverResponse = new ServerResponse("TEST RESPONSE");
ObjectMessage response = session.createObjectMessage();
response.setObject(serverResponse);
replyProducer.send(response);
} else {
System.out.println("Not a valid message for this Queue MDB");
}
} catch (JMSException e) {
e.printStackTrace();
}
}
I cannot figure out what am I missing
You are asking the wrong question here.. You should look at how to create a Connection inside any Bean.
you need to get the ConnectionFactory, and create the connection accordingly.
For more information, look at the javaee examples on the HornetQ download.
In specific look at javaee/mdb-tx-send/ when you download hornetq.
#MessageDriven(name = "MDBMessageSendTxExample",
activationConfig =
{
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/testQueue")
})
public class MDBMessageSendTxExample implements MessageListener
{
#Resource(mappedName = "java:/JmsXA")
ConnectionFactory connectionFactory;
public void onMessage(Message message)
{
Connection conn = null;
try
{
// your code here...
//Step 11. we create a JMS connection
conn = connectionFactory.createConnection();
//Step 12. We create a JMS session
Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
//Step 13. we create a producer for the reply queue
MessageProducer producer = sess.createProducer(replyDestination);
//Step 14. we create a message and send it
producer.send(sess.createTextMessage("this is a reply"));
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(conn != null)
{
try
{
conn.close();
}
catch (JMSException e)
{
}
}
}
}