Synchronous Consumer with JMS Queue - java

I want to process all the messages from a JMS Queue in Glassfish 3 in a synchronous way so I have tried to change the property Maximum Active Consumers from -1 to 1 in JMS Physical Destination in Glassfish window. I think setting this I will have only one Consumer executing OnMessage() at the same time. The problem I have reached its that when I change that property I got this error:
[I500]: Caught JVM Exception: org.xml.sax.SAXParseException: Content is not allowed in prolog.
[I500]: Caught JVM Exception: com.sun.messaging.jms.JMSException: Content is not allowed in prolog.
sendMessage Error [C4038]: com.sun.messaging.jms.JMSException: Content is not allowed in prolog.
If anyone know another way to make the method onmessage() synchronous will be appreciated. This is my Consumer Class:
#MessageDriven(mappedName = "QueueListener", activationConfig = {
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class MessageBean implements MessageListener {
#Override
public void onMessage(Message message) {
long t1 = System.currentTimeMillis();
write("MessageBean has received " + message);
try{
TextMessage result=(TextMessage)message;
String text=result.getText();
write("OTAMessageBean message ID has resolved to " + text);
int messageID=Integer.valueOf(text);
AirProcessing aP=new AirProcessing();
aP.pickup(messageID);
}
catch(Exception e){
raiseError("OTAMessageBean error " + e.getMessage());
}
long t2 = System.currentTimeMillis();
write("MessageBean has finished in " + (t2-t1));
}
}

I had the same problem, the only solution I found was to set up a Schedule which polls the messages from the queue every ten seconds:
#Stateless
public class MyReceiver {
#Resource(mappedName = "jms/MyQueueFactory")
private QueueConnectionFactory connectionFactory;
#Resource(mappedName = "jms/MyQueue")
private Queue myQueue;
private QueueConnection qc;
private QueueSession session;
private MessageConsumer consumer;
#PostConstruct
void init() {
try {
qc = connectionFactory.createQueueConnection();
session = qc.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
consumer = session.createConsumer(myQueue);
qc.start();
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
#PreDestroy
void cleanup() throws JMSException {
qc.close();
}
#Schedule(hour = "*", minute = "*", second = "*/10", persistent = false)
public void onMessage() throws JMSException {
Message message;
while ((message = consumer.receiveNoWait()) != null) {
ObjectMessage objMsg = (ObjectMessage) message;
Serializable content;
try {
content = objMsg.getObject();
//Do sth. with "content" here
message.acknowledge();
} catch (JMSException ex) {
ex.printStackTrace();
}
}
}
}

JMS is async by nature, you don't have a specific config for telling io to behave synchronously. You can simulate it by adding message delivery and consumption confirmations everywhere, but that's not really how JMS is intended to work. Try RMIenter link description here or maybe HTTP (or something on top of it like a SOAP or REST web service)

Related

JavaEE MDB "EJB Container initialisation error"

I'm pretty new to JavaEE, and I want to make a message driven bean that consumes a message and a producer that produces a message. I got most of this code from a JavaEE manual, but it doesn't want to compile on my Glassfish server.
Here is the code for the MDB:
#MessageDriven(activationConfig = {
#ActivationConfigProperty(
propertyName = "destination",
propertyValue = "myQueue"),
#ActivationConfigProperty(
propertyName = "destinationType",
propertyValue = "javax.jms.Queue")
})
public class MessageDrivenBean implements MessageListener {
public MessageDrivenBean() {
}
#Override
public void onMessage(Message message) {
try {
System.out.println("Message received: " + message.getBody(String.class));
} catch (JMSException e) {
e.printStackTrace();
}
}
}
And the code for the producer:
#Stateless
public class MsgProducer {
public MsgProducer() {
}
public void send() {
try {
// Gets the JNDI context
System.out.println("In the producer");
InitialContext jndiContext = new InitialContext();
// Looks up the administered objects
ConnectionFactory connectionFactory = (ConnectionFactory) jndiContext.lookup("jms/myConnectionFactory");
Destination queue = (Destination) jndiContext.lookup("jms/myQueue");
// Creates the needed artifacts to connect to the queue
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
// Sends a text message to the queue
TextMessage message = session.createTextMessage("Text message sent at " + new Date());
producer.send(message);
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
The method "send()" is called in another EJB.
But when I run the glassfish server the code doesn't compile and with a lot of warnings but what I can read from it is "EJB Container initialisation error". Does anybody know what to do with it?
Thank you!
PS: part of the error:
java.lang.RuntimeException: EJB Container initialization error
at org.glassfish.ejb.startup.EjbApplication.loadContainers(EjbApplication.java:210)
at org.glassfish.ejb.startup.EjbDeployer.load(EjbDeployer.java:267)
at org.glassfish.ejb.startup.EjbDeployer.load(EjbDeployer.java:75)
java.lang.RuntimeException: EJB Container initialization error
at org.glassfish.ejb.startup.EjbApplication.loadContainers(EjbApplication.java:210)
at org.glassfish.ejb.startup.EjbDeployer.load(EjbDeployer.java:267)
at org.glassfish.ejb.startup.EjbDeployer.load(EjbDeployer.java:75)
Exception while loading the app : EJB Container initialization error
java.lang.Exception
at com.sun.enterprise.connectors.inbound.ConnectorMessageBeanClient.setup(ConnectorMessageBeanClient.java:189)
at org.glassfish.ejb.mdb.MessageBeanContainer.<init>(MessageBeanContainer.java:227)
at org.glassfish.ejb.mdb.MessageBeanContainerFactory.createContainer(MessageBeanContainerFactory.java:39)
at org.glassfish.ejb.startup.EjbApplication.loadContainers(EjbApplication.java:198)
at org.glassfish.ejb.startup.EjbDeployer.load(EjbDeployer.java:267)
at org.glassfish.ejb.startup.EjbDeployer.load(EjbDeployer.java:75)
Solved it! The problem was that my propertyValue was javax.jms.Queue while I needed jakarta.jms.Queue.

Multiple queues receiving same message from virtual topic creates a deadletter entry for one queue only

I'm am using Virtual Destinations to implement Publish Subscribe model in ActiveMQ 5.15.13.
I have a virtual topic VirtualTopic and there are two queues bound to it. Each queue has its own redelivery policy. Let's say Queue 1 will retry message 2 times in case there is an exception while processing the message and Queue 2 will retry message 3 times. Post retry message will be sent to deadletter queue. I'm also using Individual Dead letter Queue strategy so that each queue has it's own deadletter queue.
I've observed that when a message is sent to VirtualTopic, the message with same message id is delivered to both the queues. I'm facing an issue where if the consumers of both queues are not able to process the message successfully. The message destined for Queue 1 is moved to deadletter queue after retrying for 2 times. But there is no deadletter queue for Queue 2, though message in Queue 2 is retried for 3 times.
Is it the expected behavior?
Code:
public class ActiveMQRedelivery {
private final ActiveMQConnectionFactory factory;
public ActiveMQRedelivery(String brokerUrl) {
factory = new ActiveMQConnectionFactory(brokerUrl);
factory.setUserName("admin");
factory.setPassword("password");
factory.setAlwaysSyncSend(false);
}
public void publish(String topicAddress, String message) {
final String topicName = "VirtualTopic." + topicAddress;
try {
final Connection producerConnection = factory.createConnection();
producerConnection.start();
final Session producerSession = producerConnection.createSession(false, AUTO_ACKNOWLEDGE);
final MessageProducer producer = producerSession.createProducer(null);
final TextMessage textMessage = producerSession.createTextMessage(message);
final Topic topic = producerSession.createTopic(topicName);
producer.send(topic, textMessage, PERSISTENT, DEFAULT_PRIORITY, DEFAULT_TIME_TO_LIVE);
} catch (JMSException e) {
throw new RuntimeException("Message could not be published", e);
}
}
public void initializeConsumer(String queueName, String topicAddress, int numOfRetry) throws JMSException {
factory.getRedeliveryPolicyMap().put(new ActiveMQQueue("*." + queueName + ".>"),
getRedeliveryPolicy(numOfRetry));
Connection connection = factory.createConnection();
connection.start();
final Session consumerSession = connection.createSession(false, CLIENT_ACKNOWLEDGE);
final Queue queue = consumerSession.createQueue("Consumer." + queueName +
".VirtualTopic." + topicAddress);
final MessageConsumer consumer = consumerSession.createConsumer(queue);
consumer.setMessageListener(message -> {
try {
System.out.println("in listener --- " + ((ActiveMQDestination)message.getJMSDestination()).getPhysicalName());
consumerSession.recover();
} catch (JMSException e) {
e.printStackTrace();
}
});
}
private RedeliveryPolicy getRedeliveryPolicy(int numOfRetry) {
final RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setInitialRedeliveryDelay(0);
redeliveryPolicy.setMaximumRedeliveries(numOfRetry);
redeliveryPolicy.setMaximumRedeliveryDelay(-1);
redeliveryPolicy.setRedeliveryDelay(0);
return redeliveryPolicy;
}
}
Test:
public class ActiveMQRedeliveryTest {
private static final String brokerUrl = "tcp://0.0.0.0:61616";
private ActiveMQRedelivery activeMQRedelivery;
#Before
public void setUp() throws Exception {
activeMQRedelivery = new ActiveMQRedelivery(brokerUrl);
}
#Test
public void testMessageRedeliveries() throws Exception {
String topicAddress = "testTopic";
activeMQRedelivery.initializeConsumer("queue1", topicAddress, 2);
activeMQRedelivery.initializeConsumer("queue2", topicAddress, 3);
activeMQRedelivery.publish(topicAddress, "TestMessage");
Thread.sleep(3000);
}
#After
public void tearDown() throws Exception {
}
}
I recently came across this problem. To fix this there are 2 attributes that needs to be added to individualDeadLetterStrategy as below
<deadLetterStrategy>
<individualDeadLetterStrategy destinationPerDurableSubscriber="true" enableAudit="false" queuePrefix="DLQ." useQueueForQueueMessages="true"/>
</deadLetterStrategy>
Explanation of attributes:
destinationPerDurableSubscriber - To enable a separate destination per durable subscriber.
enableAudit - The dead letter strategy has a message audit that is enabled by default. This prevents duplicate messages from being added to the configured DLQ. When the attribute is enabled, the same message that isn't delivered for multiple subscribers to a topic will only be placed on one of the subscriber DLQs when the destinationPerDurableSubscriber attribute is set to true i.e. say two consumers fail to acknowledge the same message for the topic, that message will only be placed on the DLQ for one consumer and not the other.

JMS transaction exception thrown in nontransaction session

Hello I have problem with my jms code when I try to send over 1000 messages to MDB. Following code:
#Stateless(mappedName = "RequestProcessingQueue")
public class RequestProcessingQueue {
private static final Logger logger = Logger.getLogger(RequestProcessingQueue.class);
#Resource(mappedName = "jmsRequestsFactory")
private ConnectionFactory connectionFactory;
#Resource(mappedName = "jmsRequestsDestination")
private Queue queue;
public void add(String participant, String password, List<Long> documents) throws JmsAppException {
try {
logger.debug("requests to process " + documents);
Connection connecton = connectionFactory.createConnection();
connecton.start();
Session session = connecton.createSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender sender = (QueueSender) session.createProducer(queue);
Message msg = msg = session.createMessage();
msg.setStringProperty("participant", participant);
msg.setStringProperty("password", password);
for (Long id : documents) {
msg.setLongProperty("request", id);
sender.send(msg);
}
sender.close();
session.close();
connecton.close();
} catch (JMSException e) {
throw new JmsAppException(e);
} catch (Throwable e) {
throw new JmsAppException("Fatal error occured while sending request to be processed", e);
}
}
}
throws
MQJMSRA_DS4001: JMSServiceException on send message:sendMessage: Sending message failed. Connection ID: 2979509408914231552 com.sun.messaging.jms.ra.DirectSession._sendMessage(DirectSession.java:1844) / sendMessage: Sending message failed. Connection ID: 2979509408914231552 com.sun.messaging.jmq.jmsserver.service.imq.IMQDirectService.sendMessage(IMQDirectService.java:1955) / transaction failed: [B4303]: The maximum number of messages [1 000] that the producer can process in a single transaction (TID=2979509408914244096) has been exceeded. Please either limit the # of messages per transaction or increase the imq.transaction.producer.maxNumMsgs property. com.sun.messaging.jmq.jmsserver.data.handlers.DataHandler.routeMessage(DataHandler.java:467)'}
at jms.example.RequestProcessingQueue.add(RequestProcessingQueue.java:48)
I do not understand why cus when I create session I pass false as first param indicating that session is non transactional mode.
Your code does not work because the basic JMS API was designed to work in any environment, not just from within an EJB container. Runtime environment programming restrictions and behaviour are described in the EJB specifications and JavaDoc, in particular javax.jms.Connection.createSession(boolean transacted, int acknowledgeMode).
Your code can be simplified (assuming you're using at least Java 7) to:
#TransactionAttribute(TransactionAttributeType.NOTSUPPORTED)
public void add(String participant, String password, List<Long> documents) throws OgnivoException {
try (Connection connection = connectionFactory.createConnection();
Session session = connection.createSession();
// session.start() not required
MessageProducer sender = session.createProducer(queue)) {
logger.debug("requests to process " + documents);
for (Long id : documents) {
Message msg = msg = session.createMessage();
msg.setStringProperty("participant", participant);
msg.setStringProperty("password", password);
msg.setLongProperty("request", id);
sender.send(msg);
}
} catch (JMSException e) {
throw new JmsAppException(e);
}
// Don't catch throwable because it hides bugs
}
Remember that EJB methods are automatically associated with a transaction unless you specify otherwise. Additionally, be sure to check the javadoc for javax.jms.Connection.createSession() and associated methods, particularly the sections describing behaviour in different runtime environments.

JMS multiple durable subscription to one topic

I started JMS for a week now. I created JMS using Netbeans,maven and glassfish.
I have one producer and one durable consumer and I wanted to add another durable consumer to the same topic(not queue). Is it possible to do so?
because I want all the consumers consume all the message being sent by the producer whether the consumers are offline or not.
Any advice?
Thanks
public class DurableReceive {
#Resource(lookup = "jms/myDurableConnectionFactory")
private static ConnectionFactory connectionFactory;
#Resource(lookup = "jms/myNewTopic")
private static Topic topic;
public static void main(String[] args) {
Destination dest = (Destination) topic;
JMSConsumer consumer;
boolean messageReceived = false;
String message;
System.out.println("Waiting for messages...");
try (JMSContext context = connectionFactory.createContext();) {
consumer = context.createDurableConsumer(topic, "Subscriber1");
while (!messageReceived) {
message = consumer.receiveBody(String.class);
if (message != null) {
System.out.print("Received the following message: " + message);
System.out.println("(Received date: " + new Date() + ")\n");
} else {
messageReceived = true;
}
}
} catch (JMSRuntimeException e) {
System.err.println("##$%RuntimeException occurred: " + e.toString());
System.exit(1);
}
}
}
You can set different clientID for different durable consumers. Jms-broker uses combination of subscriptionName and clientId to identify the unique client (so if your subscriber have unique clientID - it can receive own messages). You can set clientID in your JmsContext.

Signal a rollback from a JMS MessageListener

I've been working with JMS and ActiveMQ. Everything is working wonders. I am not using spring, nor can I.
The interface javax.jms.MessageListener has only one method, onMessage. From within a implementation, there is a chance an exception will be thrown. If in fact an exception gets thrown, then I say the message wasn't properly processed and needs to be re-tried. So, I need ActiveMQ to wait for a little while and then, retry. i.e. I need the thrown exception to rollback the JMS transaction.
How can I accomplish such a behaviour?
Maybe there is some configuration in ActiveMQ I wasn't able to find.
Or... maybe could do away with registering MessageListeners to consumers and consume the messages myself, in a a loop like:
while (true) {
// ... some administrative stuff like ...
session = connection.createSesstion(true, SESSION_TRANSACTED)
try {
Message m = receiver.receive(queue, 1000L);
theMessageListener.onMessage(m);
session.commit();
} catch (Exception e) {
session.rollback();
Thread.sleep(someTimeDefinedSomewhereElse);
}
// ... some more administrative stuff
}
in a couple of threads, instead of registering the listener.
Or... I could somehow decorate/AOP/byte-manipulate the MessageListeners to do this themselves.
What route would you take and why?
note: I don't have full control over the MessageListeners code.
EDIT
A test for proof of concept:
#Test
#Ignore("Interactive test, just a proof of concept")
public void transaccionConListener() throws Exception {
final AtomicInteger atomicInteger = new AtomicInteger(0);
BrokerService brokerService = new BrokerService();
String bindAddress = "vm://localhost";
brokerService.addConnector(bindAddress);
brokerService.setPersistenceAdapter(new MemoryPersistenceAdapter());
brokerService.setUseJmx(false);
brokerService.start();
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(bindAddress);
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setInitialRedeliveryDelay(500);
redeliveryPolicy.setBackOffMultiplier(2);
redeliveryPolicy.setUseExponentialBackOff(true);
redeliveryPolicy.setMaximumRedeliveries(2);
activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy);
activeMQConnectionFactory.setUseRetroactiveConsumer(true);
activeMQConnectionFactory.setClientIDPrefix("ID");
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(activeMQConnectionFactory);
pooledConnectionFactory.start();
Connection connection = pooledConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
Queue helloQueue = session.createQueue("Hello");
MessageConsumer consumer = session.createConsumer(helloQueue);
consumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
switch (atomicInteger.getAndIncrement()) {
case 0:
System.out.println("OK, first message received " + textMessage.getText());
message.acknowledge();
break;
case 1:
System.out.println("NOPE, second must be retried " + textMessage.getText());
throw new RuntimeException("I failed, aaaaah");
case 2:
System.out.println("OK, second message received " + textMessage.getText());
message.acknowledge();
}
} catch (JMSException e) {
e.printStackTrace(System.out);
}
}
});
connection.start();
{
// A client sends two messages...
Connection connection1 = pooledConnectionFactory.createConnection();
Session session1 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection1.start();
MessageProducer producer = session1.createProducer(helloQueue);
producer.send(session1.createTextMessage("Hello World 1"));
producer.send(session1.createTextMessage("Hello World 2"));
producer.close();
session1.close();
connection1.stop();
connection1.close();
}
JOptionPane.showInputDialog("I will wait, you watch the log...");
consumer.close();
session.close();
connection.stop();
connection.close();
pooledConnectionFactory.stop();
brokerService.stop();
assertEquals(3, atomicInteger.get());
}
If you want to use SESSION_TRANSACTED as your acknowledgement mode, then you need to setup a RedeliveryPolicy on your Connection/ConnectionFactory. This page on ActiveMQ's website also contains some good info for what you might need to do.
Since you aren't using Spring, you can setup a RedeliveryPolicy with something similar to the following code (taken from one of the above links):
RedeliveryPolicy policy = connection.getRedeliveryPolicy();
policy.setInitialRedeliveryDelay(500);
policy.setBackOffMultiplier(2);
policy.setUseExponentialBackOff(true);
policy.setMaximumRedeliveries(2);
Edit
Taking your code snippet added to the answer, the following shows how this works with transactions. Try this code with the Session.rollback() method commented out and you'll see that using SESION_TRANSACTED and Session.commit/rollback works as expected:
#Test
public void test() throws Exception {
final AtomicInteger atomicInteger = new AtomicInteger(0);
BrokerService brokerService = new BrokerService();
String bindAddress = "vm://localhost";
brokerService.addConnector(bindAddress);
brokerService.setPersistenceAdapter(new MemoryPersistenceAdapter());
brokerService.setUseJmx(false);
brokerService.start();
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(bindAddress);
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setInitialRedeliveryDelay(500);
redeliveryPolicy.setBackOffMultiplier(2);
redeliveryPolicy.setUseExponentialBackOff(true);
redeliveryPolicy.setMaximumRedeliveries(2);
activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy);
activeMQConnectionFactory.setUseRetroactiveConsumer(true);
activeMQConnectionFactory.setClientIDPrefix("ID");
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(activeMQConnectionFactory);
pooledConnectionFactory.start();
Connection connection = pooledConnectionFactory.createConnection();
final Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
Queue helloQueue = session.createQueue("Hello");
MessageConsumer consumer = session.createConsumer(helloQueue);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
switch (atomicInteger.getAndIncrement()) {
case 0:
System.out.println("OK, first message received " + textMessage.getText());
session.commit();
break;
case 1:
System.out.println("NOPE, second must be retried " + textMessage.getText());
session.rollback();
throw new RuntimeException("I failed, aaaaah");
case 2:
System.out.println("OK, second message received " + textMessage.getText());
session.commit();
}
} catch (JMSException e) {
e.printStackTrace(System.out);
}
}
});
connection.start();
{
// A client sends two messages...
Connection connection1 = pooledConnectionFactory.createConnection();
Session session1 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection1.start();
MessageProducer producer = session1.createProducer(helloQueue);
producer.send(session1.createTextMessage("Hello World 1"));
producer.send(session1.createTextMessage("Hello World 2"));
producer.close();
session1.close();
connection1.stop();
connection1.close();
}
JOptionPane.showInputDialog("I will wait, you watch the log...");
consumer.close();
session.close();
connection.stop();
connection.close();
pooledConnectionFactory.stop();
assertEquals(3, atomicInteger.get());
}
}
You need to set the acknowledgment mode to Session.CLIENT_ACKNOWLEDGE, the client acknowledges a consumed message by calling the message's acknowledge method.
QueueSession session = connection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
Then, after processing the message to need to call the Message.acknowledge() method in order to remove that message.
Message message = ...;
// Processing message
message.acknowledge();
A little late, but here goes -
I would not use a MessageListener but rather a global pools to manage listening and processing.
ListeningPool -> listener -> submit processing task -> ProcessingPool -> execute and acknowledge or close without acknowledgment.
Maintain 2 thread pools, one for listeners and one for processors.
Have a listening Runnable implementation that listens to a queue in a while true loop, and consumer.receive(timeout) method. In the finally block, close connection, session and consumer if there was no message received. If a message is received, submit a task to processing pool with all the conn, session, message and consumer arguments.
Have a processing implementation that takes in the message, connection, session and consumer. Do your processing and acknowledge if all ok. If not, close without acknowledgement. This would trigger a redelivery according to your server's redelivery policy.
Initialize your listening pool with all the Listener Tasks, listening for messages, each for one queue. Initialize your processing pool with parameters acceptable to your application runtime.
public class CustomMessageListener implements Runnable {
private ConnectionFactory connectionFactory;
private MessageProcessor processor;
private long backOff;
private boolean stopped = false;
private Executor processPool;
public CustomMessageListener(ConnectionFactory connectionFactory,
long backOff, MessageProcessor processor, Executor processPool) {
this.connectionFactory = connectionFactory;
this.backOff = backOff;
this.processor = processor;
this.processPool = processPool;
}
#Override
public void run() {
while (!stopped) {
listen();
}
}
public void stop() {
this.stopped = true;
}
public void listen() {
Connection c = null;
Session s = null;
MessageConsumer consumer = null;
boolean received = false;
try {
c = connectionFactory.createConnection();
s = c.createSession(false, Session.CLIENT_ACKNOWLEDGE);
consumer = s.createConsumer(...);
Message message = consumer.receive(backOff); // waits maximum backOff ms for a message
if (message != null) {
received = true;
// submit a task to processing pool...
executor.submit(processor.process(message, s, consumer, c));
}
} catch (JMSException ex) {
// log your exception
} finally {
if (!received) {
// close conn, session, consumer
}
}
}
}
public class MessageProcessor {
public Runnable process(Message msg, Session s, MessageConsumer consumer, Connection conn) {
return () - > {
try {
//do your processing
msg.acknowledge(); // done
} catch (JMSException ex) {
// log your exception
} finally {
// close your resources
}
};
}
}
You can call stop() to stop listening for more messages, for a graceful shutdown. Include a queueName in the constructor to listen for a particular queue.
If your session is transacted,then "acknowledgeMode" is ignored anyways..So, just leave your session transacted and use session.rollback and session.commit to commit or rollback your transaction.

Categories