JMS - Send message sychronously from message-driven bean - java

I have a message-driven bean which receives messages from a queue, processes them, and sends messages to another queue, with
onMessage(Message inputMessage) {
... Message processing stuff...
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Message outputMessage = session.createObjectMessage();
outputMessage.setJMSCorrelationID(uniqueId);
MessageProducer messageProducer = session.createProducer(outputQueue);
messageProducer.send(outputMessage);
... Some more processing...
QueueBrowser browser;
browser = session.createBrowser(outputQueue,
String.format("JMSCorrelationID='%s'", uniqueId);
}
Then, I check the queue for the uniqueId, but the message does not yet appear in the queue. After experimenting a little, I found out that the message appear in the output queue only after the onMessage method has returned.
Is this a bug? Is there a way to send the outputMessage immediately, so that I can be sure that after messageProducer.send(outputMessage) the message does appear in the outputQueue?

Seems like the flip-side to the situation here - JMS rollback
You're wanting to avoid the transactional behavior -- send immediately unrelated to the MDB transaction.
Reading the JavaEE 7 Connection.createSession() docs it sounds like there's not a good way to create a session detached from the MDB's JTA transaction. The docs go so far as to say that #schtever's answer of using session.commit() won't work.
If all this is true, maybe create some additional method that does the JMS send call. Set this additional method as transaction NOT_SUPPORTED or maybe REQUIRES_NEW.

Issue a session.commit(); after the messageProducer.send(outputMessage);

When running in an application server the JMS operations enlist in any global transaction. The solution to this is to do the send in another transactional context. The simplest thing to do is move the send into an EJB with a transaction attribute of requires new.

Related

Transaction synchronization not active for incoming JMS message

I am using Apache Camel to send messages to my Java service. I have kept transacted=true on consumer route. I also need to send e-mail on successfully processing of JMS messages.
I am using below code to register synchronization and send e-mail only after transaction is committed.
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
#Override
public void afterCommit(){
sendMail(mailBody);
}
});
Problem: incoming transaction from Camel is not synchronized and I am getting
java.lang.IllegalStateException: Transaction synchronization is not active
I tried calling transactionsynchronizationmanager.initsynchronization() - I am not getting any exception but afterCommit() method is never called.
transactionsynchronizationmanager.initsynchronization();
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
#Override
public void afterCommit(){
sendMail(mailBody); //never called
}
});
Same code is working when request is received via spring mvc controller (through Spring Transaction).
You likely need to turn on transacted on the route to enable spring transaction. The option transacted=true on the JMS endpoint is NOT spring-transaction, but its only for JMS acknowledge mode to be set as transacted. They are not the same.
So in your Camel route, setup spring transaction as well, eg
from jms
transacted
See more details in the Camel docs: http://camel.apache.org/transactional-client.html or even better if you have a copy of the Camel in Action book (1st or 2nd ed) then it has a fully chapter devoted to transactions.

Configure activemq to be transactional

It´s more of a conceptual question: I currently have a working activemq queue which is consumed by a Java Spring application. Now I want the queue not to permanently delete the messages until the Java app tells it the message has been correctly saved in DB. After reading documentation I get I have to do it transactional and usa the commit() / rollback() methods. Correct me if I'm wrong here.
My problem comes with every example I find over the internet telling me to configure the app to work this or that way, but my nose tells me I should instead be setting up the queue itself to work the way I want. And I can't find the way to do it.
Otherwise, is the queue just working in different ways depending on how the consumer application is configured to work? What am I getting wrong?
Thanks in advance
The queue it self is not aware of any transactional system but you can pass the 1st parameter boolean to true to create a transactional session but i propose the INDIVIDUAL_ACKNOWLEDGE when creating a session because you can manage messages one by one. Can be set on spring jms DefaultMessageListenerContainer .
ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE
And calling this method to ack a message, unless the method is not called the message is considered as dispatched but not ack.
ActiveMQTextMessage.acknowledge();
UPDATE:
ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE can be used like this :
onMessage(ActiveMQTextMessage message)
try {
do some stuff in the database
jdbc.commit(); (unless auto-commit is enabled on the JDBC)
message.acknowledge();
}
catch (Exception e) {
}
There are 2 kinds of transaction support in ActiveMQ.
JMS transactions - the commit() / rollback() methods on a Session (which is like doing commit() / rollback() on a JDBC connection)
XA Transactions - where the XASession acts as an XAResource by communicating with the Message Broker, rather like a JDBC Connection takes place in an XA transaction by communicating with the database.
http://activemq.apache.org/how-do-transactions-work.html
Should I use XA transactions (two phase commit?)
A common use of JMS is to consume messages from a queue or topic, process them using a database or EJB, then acknowledge / commit the message.
If you are using more than one resource; e.g. reading a JMS message and writing to a database, you really should use XA - its purpose is to provide atomic transactions for multiple transactional resources. For example there is a small window from when you complete updating the database and your changes are committed up to the point at which you commit/acknowledge the message; if there is a network/hardware/process failure inside that window, the message will be redelivered and you may end up processing duplicates.
http://activemq.apache.org/should-i-use-xa.html

How reliable is JMS rollback?

Code...
#Transactional
#JmsListener(destination = "QueueA")
public void process(String input) {
doSomethingWhichMayThrowException(input);
}
Consider following situation where...
Transaction is started (using Spring #Transactional annotation)
Persistent JMS message is read from QueueA (Queue use disk as message storage)
Disk is full and do not accept any write operations
Exception happens and transaction is rolled back
Is message lost?
If it's not then how message is read from queue under transaction (step 2)?
Is some kind of a queue browser used so message is read from queue but not consumed?
Is message lost?
No, the message is NOT lost as the transaction is rolledback.
If it's not then how message is read from queue under transaction
(step 2) ?
Once after the message listener's process()/onMessage() method completes and returns with a success or exception, then internally message acknowledgment (default is AUTO_ACKNOWLEDGE) happens (which is the last thing implicitly happens) to the JMS provider(IBMMQ, ActiveMQ, SonicMQ, etc..) which tells that the transaction is successful or not.
If the Transaction is successful, JMS provider deletes the message from the queue/topic.
If the Transaction is NOT successful, JMS provider preserve the message as is (until message TimetoLive expires).
Is some kind of a queue browser used so message is read from queue but
not consumed ?
You can think that it is like queue browser concept, but it is upto the implementation of the JMS provider how do they implement this internally. In order to achieve this, the message broker just reads the message content, but do not delete the actual message from the queue/topic until the acknowledgement is received from the message listener's process()/onMessage() method for the current transaction.

Is it possible to stop and restart a durable subscription using spring jms?

I'm using the following DefaultMessageListenerContainer to create a durable subscription to get messages even in downtimes which really works well.
#Bean
ConnectionFactory connectionFactory() {
SingleConnectionFactory connectionFactory = new SingleConnectionFactory(new ActiveMQConnectionFactory(
AMQ_BROKER_URL));
connectionFactory.setClientId(CLIENT_ID);
return connectionFactory;
}
#Bean
DefaultMessageListenerContainer container(final MessageListener messageListener,
final ConnectionFactory connectionFactory) {
return new DefaultMessageListenerContainer() {
{
setMessageListener(messageListener);
setConnectionFactory(connectionFactory);
setDestinationName(JMS_TARGET);
setPubSubDomain(true);
setSessionTransacted(true);
setSubscriptionDurable(true);
setDurableSubscriptionName(SUBSCRIPTION_ID);
setConcurrentConsumers(1);
}
};
}
The question is: What is the best way to remove the subscription when I don't need it anymore? Would it even be possible to temporarily remove the subscription (and miss some messages), but enable it later again?
The only way which worked so far, was to shutdown the DMLC and call unsubscribe afterwards.
dmlc.shutdown();
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
session.unsubscribe(SUBSCRIPTION_ID);
Is that a sensible solution? How could the subscription be reinitiated?
I've already seen this answer but I really don't know how to do this? Or would it be even better to subscribe and unsubscribe in a total different way?
Before I suggest my answer, allow me a word of caution: temporarily removing a durable subscription sounds a little bit like you don't really need a durable subscription. Durable subscriptions are for Consumers which need to receive messages that were sent while they are offline. If you need the messages only sometimes (say, when your consumer is connected), a non-durable topic is the item of choice.
Note that this only makes sense for Topics (1:n communication, pub/sub). Queues work slightly different as they are used for 1:1 communication (advanced techniques like load balancing set aside for now).
Durable topics incur a notable resource overhead on the message broker and removing and recreating subscriptions might lead to expensive initialization over and over again.
Now if you really want to (temporarily) unsubscribe from your durable topic (also works for "normal" topics), you'll have to extend DefaultMessageListenerContainer:
public class MyContainer extends DefaultMessageListenerContainer {
public Session retrieveSession() {
return getSession(); // this is protected, so we wrap it (could also make getSession public)
}
}
When you instantiate MyContainer (instead of DefaultMessageListenerContainer) in the container method, make sure to store the reference:
protected MyContainer listenerContainer;
...
listenerContainer = new MyContainer(...);
return listenerContainer;
You can then just call listenerContainer.retrieveSession().unsubscribe(...) to unsubscribe.
Please also note that you may only call this after all the session's consumers to that topic are closed and there are no messages in transit (direct quote from the documentation: "It is erroneous for a client to delete a durable subscription while there is an active (not closed) consumer for the subscription, or while a consumed message is part of a pending transaction or has not been acknowledged in the session."

JMS and MDB with setRollbackOnly

I have a java class which consumes messages from a queue, sending HTTP calls to some urls. I have made some search on google and also on stackoverflow (and really sorry if i have missed any sources mentioning about the problem) but couldnt find anything in details about setRollbackOnly call.
My question is... in case I rollback, the message which is consumed from the queue will be blocking the rest of the queue and will be looping until it is processed successfully or it will be requeued at the end of the current queue?
My code which I use for consuming from the queue and sending HTTP calls is below and the whole application is running on Glassfish server:
public class RequestSenderBean implements MessageListener
{
#Resource
private MessageDrivenContext mdbContext;
public RequestSenderBean(){}
public void onMessage(final Message message)
{
try
{
if(message instanceof ObjectMessage)
{
String responseOfCall=sendHttpPost(URL, PARAMS_FROM_MESSAGE);
if(responseOfCall.startsWith("Success"))
{
//Everything is OK, do some stuff
}
else if(responseOfCall.startsWith("Failure"))
{
//Failure, do some other stuff
}
}
catch(final Exception e)
{
e.printStackTrace();
mdbContext.setRollbackOnly();
}
}
}
This is fundamental JMS/messaging knowledge.
Queues implement "load balancing" scenarios, whereby a message hits a queue and is dequed to be processed by one consumer. Increasing the number of consumers increases potential throughput of that queue's processing. Each message on a queue will be processed by one and only one consumer.
Topics provide publish-subscribe semantics: all consumers of a topic will receive the message that is pushed to the topic.
With that in mind, once a message is dequed and handed (transactionally) to a consumer, it is by no means blocking the rest of the queue if it is asynchronous (as is the case with MDBs).
As the Java EE Tutorial states:
Message Consumption
Messaging products are inherently asynchronous: There is no fundamental timing dependency between the production and the consumption of a message. However, the JMS specification uses this term in a more precise sense. Messages can be consumed in either of two ways:
Synchronously: A subscriber or a receiver explicitly fetches the message from the destination by calling the receive method. The receive method can block until a message arrives or can time out if a message does not arrive within a specified time limit.
Asynchronously: A client can register a message listener with a consumer. A message listener is similar to an event listener. Whenever a message arrives at the destination, the JMS provider delivers the message by calling the listener’s onMessage method, which acts on the contents of the message.
Because you use a MessageListener which is by definition asynchronous, you are not blocking the queue or its subsequent processing.
Also from the tutorial is the following:
Using Session Beans to Produce and to Synchronously Receive Messages
An application that produces messages or synchronously receives them can use a session bean to perform these operations. The example in An Application That Uses the JMS API with a Session Bean uses a stateless session bean to publish messages to a topic.
Because a blocking synchronous receive ties up server resources, it is not a good programming practice to use such a receive call in an enterprise bean. Instead, use a timed synchronous receive, or use a message-driven bean to receive messages asynchronously. For details about blocking and timed synchronous receives, see Writing the Clients for the Synchronous Receive Example.
As for message failure, it depends on how your queue is configured. You can set error-queues (in the case of containers like Glassfish or Weblogic) that failed messages are pushed to for later inspection. In your case, you're using setRollbackOnly which is handled thus:
7.1.2 Coding the Message-Driven Bean: MessageBean.java
The message-driven bean class, MessageBean.java, implements the
methods setMessageDrivenContext, ejbCreate, onMessage, and ejbRemove.
The onMessage method, almost identical to that of TextListener.java,
casts the incoming message to a TextMessage and displays the text. The
only significant difference is that it calls the
MessageDrivenContext.setRollbackOnly method in case of an exception.
This method rolls back the transaction so that the message will be
redelivered.
I recommend you read the Java EE Tutorial as well as the Enterprise Integration Patterns book which covers messaging concepts in good detail that's also product/technology-agnostic.

Categories