I have set up a simple request/reply type scenario (JavaEE 7/Netbeans 7 with Glassfish) with code as follows:
This is a stateless session bean performing the JMS producer role
destination and factory are injected into the EJB.
public void doStuff(int id) {
try {
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
ObjectMessage message = session.createObjectMessage();
MyObject obj = new MyObject(id);
connection.start();
TemporaryQueue replyQueue = session.createTemporaryQueue();
MessageConsumer consumer = session.createConsumer(replyQueue);
message.setJMSReplyTo(replyQueue);
message.setObject(obj);
producer.send(message, DeliveryMode.PERSISTENT, Message.DEFAULT_PRIORITY, 1800000);
Message reply = consumer.receive();
producer.close();
session.close();
connection.close();
} catch (JMSException ex) {
}
}
My problem is that when consumer.receive() is called, execution blocks as expected - however the consumer of this message never runs. The consumer of the original message (it's an MDB) is located in another netbeans project deployed on the same Glassfish server.
I have tried debugging both projects (both the producer and consumer) and it seems like while my producer project is blocking, my consumer project doesn't do anything at all.
If I change consumer.recieve() to something like consumer.receive(20000) then as expected nothing happens for 20 seconds - but as soon as the timeout expires all of a sudden I get a debugger breakpoint hit on my consumer project and it processes as normal. But of course no reply is sent back to my producer project because it's no longer listening!
My other project (the consumer project) looks something like this:
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/myQueue")
})
---------------------
#Override
public void onMessage(Message message) {
try {
ObjectMessage objMessage = (ObjectMessage) message;
MyObject obj = (MyObject) objMessage.getObject();
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(message.getJMSReplyTo());
connection.start();
try {
myMethod(obj);
} catch (Exception e) {
Message response = session.createTextMessage(e.getMessage());
producer.send(message.getJMSReplyTo(), response);
}
} catch (JMSException ex) {
}
}
How can I get the receive() method to work correctly? I need it to pause for a reply, but while pausing I need my other project to continue working - and I'd assume they're on different threads anyway if they're two separately deployed projects.
I eventually discovered that I was getting this issue because my doStuff() method was running a Container Managed Transaction.
In addition to this, you can only perform one action per transaction (eg: Only one of the request/receive)
So when I was calling producer.send(), the message wasn't actually sent because the transaction had not finished yet (the method was not finished). It then went on to the receive call and blocked there since no message was sent.
I worked around this by making my doStuff() method a Bean Managed Transaction, I injected a UserTransaction object with the #Resource annotation on the bean.
I went to bound the send action by UserTransaction.begin and UserTransaction.commit and I repeated this to bind my second receive action - it worked as expected.
This provided me with a clear explanation on CMTs: http://docs.oracle.com/cd/E19798-01/821-1841/bncij/index.html
Related
I am trying to connect to solace queues and simply read messages from them. However, I am able to read the messages but the messages are not getting removed from the queue.
Below is the code which I tried:
public void clearMessages() throws Exception {
// Programmatically create the connection factory using default settings
// Create connection to the Solace router
SolXAConnectionFactoryImpl connectionFactory = returnConnFactory();
XAConnection connection = connectionFactory.createXAConnection();
XASession session = connection.createXASession();
Queue queue = session.createQueue(QUEUE_NAME);
connection.start();
MessageConsumer messageConsumer = session.createConsumer(queue);
//session.b
messageConsumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
if(message instanceof SolTextMessage) {
SolTextMessage solTextMessage =(SolTextMessage)message;
try {
System.out.println("Message cleared is : "+solTextMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
System.out.println("Message Content: %s"+ SolJmsUtility.dumpMessage(message));
}
latch.countDown();
}
});
latch.await(120,TimeUnit.SECONDS);
connection.stop();
messageConsumer.close();
session.close();
connection.close();
}
Here latch is the object of CountDownLatch which is initialized as:
CountDownLatch latch = new CountDownLatch(2);
You need to commit the XA transaction in order for messages to be consumed.
In JMS, the function call is XAResource.commit(xid, true)
Also, is there a reason to use the CountDownLatch?
If you would like to consume messages synchronously, you can choose not to set a message listener and call MessageConsumer.receive()
Solace does provide a basic sample showing how to make use of XA transactions.
Refer to XATransactions.java in the samples directory of the API.
Note that the sample code is manually managing the XA transaction by calling the relevant XAResource methods such as XAResource.commit().
XA transactions are usually used within Java EE application servers that contain a transaction manager to manage the lifecycle of XA transactions.
I have a JAVA application which creates consumers that listen to rabbitmq . I need to know the started consumer is still working fine and if not then i need to restart the consumer.
Is their any way i can do that. Currently my main application creates an Executor thread pool and passes this executor while creating new connection.
ExecutorService executor = Executors.newFixedThreadPool(30);
Connection connection = factory.newConnection(executor);
The main method then create 30 consumerApp object by calling constructor with new channel as argument and call the listen() method
for(int i=0;i<=30;i++) {
ConsumerApp consumer = new ConsumerApp(i,connection.createChanell());
consumer.listen() }
The listen method in consumerApp listen to a queue and start a DefaultConsumer Object which simply prints the received message
listen() {
try {
channel.queueDeclare("test-queue-name", false, false, false, null);
}
catch {
System.out.println("Exception on creating Queue")
}
Consumer consumer = new DefaultConsumer(this.channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received Message in consumer '"+consumerId+" "+ message + "'");
}
};
//Now starting the consumer
try {
channel.basicConsume(QUEUE_NAME, true, consumer);
}
catch (ShutdownSignalException | IOException ex) {
ex.printStackTrace();
}
}
I want to know is their any way i can check the consumer is active . My idea is to catch the shutdown signal exception and recreate the consumer object and recall the listen method . Is this necessary as rabbitmq auto recovers and connnect back. ? But how can i ensure this ?
Is this any way achievable using the threadpool passed to rabbitmq connector.
I am using latest version of rabbitmq client 5.3.0
Consumer has different methods that can help you track the state of your consumer. You're likely to be interested in handleConsumeOk and in handleCancel.
Automatic connection recovery will indeed re-register consumers after a connection failure, but that doesn't prevent you from following their state manually to e.g. expose some information on JMX.
I'm using ActiveMQ. My consumer code is called from a main method. Once the main class terminates, I expect JMSMessageListener to have been registered on the queue and whenever there is a message on "TestTopic", onMessage to be called, which is not happening.
//JMS Consumer
public class JMSConsumer {
public void consume() {
String url = "tcp://localhost:61616";
ConnectionFactory factory = new ActiveMQConnectionFactory(url);
try {
Connection connection = factory.createConnection();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("TestTopic");
MessageConsumer consumer = session.createConsumer(topic);
JMSMessageListener listener = new JMSMessageListener();
consumer.setMessageListener(listener);
connection.start();
} catch (JMSException exp) {}
}
}
//JMS Message Listener
public class JMSMessageListener implements MessageListener {
#Override
public void onMessage(javax.jms.Message msg) {
System.out.println(msg.toString());
}
}
Is this because the main thread has been terminated and the listener is not live anymore? I thought with setMessageListener, the above code should internally create a thread that should always be running.
Is this because the main thread has been terminated and the listener is not live anymore? I thought with setMessageListener, the above code should internally create a thread that should always be running.
setMessageListener does not create a new thread. When you create a new Session from the Connection, a new thread is created. So yes even if the main thread terminates, the Session thread should continue running.
Your code looks correct to me. Are you sure that messages exist on that topic? Also only messages published to the topic after the consumer comes live will be received.
Currently there is a embedded broker running in my application, and I want to consume the queue within the same application in different thread. It works when I use TCP Transport, but I found I can not use VM Transport when the broker and the consumer in the same application. (It works if I create another process for consumer) Is there a better way to do in my situation?
Broker config with Spring
<amq:broker id="myBroker" brokerName="myBroker">
<amq:transportConnectors>
<amq:transportConnector uri="tcp://localhost:7777" />
<amq:transportConnector uri="vm://myBroker" />
</amq:transportConnectors>
</amq:broker>
Consumer
public class TestConsumer {
private static String brokerURL = "tcp://localhost:7777";
private static transient ConnectionFactory factory;
private transient Connection connection;
private transient Session session;
public TestConsumer() throws JMSException {
factory = new ActiveMQConnectionFactory(brokerURL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
public Session getSession() {
return session;
}
}
Listener
public class Listener implements MessageListener {
public void onMessage(Message message) {
try {
//do something here
System.out.println(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
In Main
TestConsumer consumer = new TestConsumer();
Destination destination = consumer.getSession().createQueue("TESTQUEUE");
MessageConsumer messageConsumer = consumer.getSession().createConsumer(destination);
messageConsumer.setMessageListener(new Listener());
It works when brokerURL is "tcp:localhost:7777" or is "vm://myBroker" but Broker and Consumer are in different processes. I just can not use VM Transport when the two are in the same application.
Try to use VM trnasport. If I understand you correctly your application sends and receives messages itself. In this case VM transport is the best choice: it even does not exit your JVM and transfers messages using direct API calls.
See this article for details.
http://activemq.apache.org/configuring-transports.html
I'm testing open MQ for send and receive messages in my project. I have no problem to configure it to send a synchronous message, but i can't find any way in the official documentation to configure the message to be consumed 15 minutes after the producer send a message, and continue call the consumer if an error appears.
offical documentation: http://dlc.sun.com/pdf/819-7757/819-7757.pdf
my method whom send a message
public void sendMessage(EntradaPrecomven entrada){
try{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:///C:/mqteste");
// Create the initial context.
Context ctx = new InitialContext(env);
// Look up the connection factory object in the JNDI object store.
autenticisFactory = (ConnectionFactory) ctx.lookup(CF_LOOKUP_NAME);
mdbConn = autenticisFactory.createConnection();
mdbSession = mdbConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination) ctx.lookup(DEST_LOOKUP_NAME);
MessageProducer myProducer = mdbSession.createProducer(destination);
ObjectMessage outMsg = mdbSession.createObjectMessage(entrada);
outMsg.setJMSRedelivered(Boolean.TRUE);
myProducer.send(outMsg);
consumidor = mdbSession.createConsumer(destination);
MessageMDB myListener = new MessageMDB();
consumidor.setMessageListener(myListener);
mdbConn.start();
mdbConn.close();
}catch(Exception e){
try {
mdbSession.rollback();
} catch (JMSException e1) {}
}
}
My listener:
#Override
public void onMessage(Message msg) {
ObjectMessage objMessage = (ObjectMessage) msg;
try {
System.out.println("Received Phone Call:" + objMessage.getJMSRedelivered());
throw new JMSException("TESTE");
} catch (JMSException e) {
e.printStackTrace();
}
}
So, when i call mdbConn.start() the sendMessage() is called, but i want to call 15 minutes after the call. And whatever it sendMessage() does, the message is always removed from the queue. How can i keep the messagen in queue to be called later ?
Thanks!
The message is removed from the broker queue due to the fact that the session you are using is set to auto acknowledge.
mdbSession = mdbConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
This will automatically send an acknowledgement to the broker that the listener has received a message for the consumer with which it is associated once the onMessage() method has executed to completion. This then results in the message being removed from the queue.
If you manually take over the acknowledgement process you can choose to only acknowledge the receipt of the message at a time of your choosing (be that 15 minutes later or whatever criteria you have for the consuming client).
Setting the Session Session.CLIENT_ACKNOWLEDGE will allow you to do this but then you will have to manually send an acknowledge in your consumer code. By calling acknowledge on the message msg.acknowledge() inside your onMessage() method within your listener.
This will then acknowledge the receipt of messages consumed within that session and remove them from the queue.
Pages 46 and 65 in the pdf you quoted are useful for more information on this as is the api