How must I handle exceptions inside a mdb? I have the funny feeling that the exception happens after the try catch block so I'm not able to catch and log it. Glassfish v3 decides to repeat the whole message. It runns into a infinite loop and writes lot's of logfiles on the harddrive.
I'm using Glassfishv3.01 + Eclipselink 2.0.1
public class SaveAdMessageDrivenBean implements MessageListener {
@PersistenceContext(unitName="QIS")
private EntityManager em;
@Resource
private MessageDrivenContext mdc;
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage obj = (ObjectMessage)message;
AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
save(alyzres);
}
} catch (Throwable e) {
mdc.setRollbackOnly();
log.log(Level.SEVERE, e);
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
Some s = em.find(Some.class, somepk);
s.setSomeField("newvalue");
// SQL Exception happens after leaving this method because of missing field for ex.
}
}
If the bean method encounters a runtime exception or error, it should simply propagate the error from the bean method to the container. If the bean method performs an operation that results in a checked exception that the bean method cannot recover, the bean method should throw the javax.
A message-driven bean (MDB) is a consumer of messages from a Java™ Message Service (JMS) provider. An MDB is invoked on arrival of a message at the destination or endpoint that the MDB services. MDB instances are anonymous, and therefore, all instances are equivalent when not actively servicing a client message.
All message-driven beans MUST implement, directly or indirectly, the MessageDrivenBean interface. The class MUST be defined as public and it cannot be defined as final nor abstract.
The onMessage method is called by the bean's container when a message has arrived for the bean to service. This method contains the business logic that handles the processing of the message. It is the message-driven bean's responsibility to parse the message and perform the necessary business logic.
You got a bad case of message poisoning...
The main issues I see are that:
save()
method in your onMessage()
: this means thet the container has no way to inject the proper transaction handling proxy around the save
methodsave()
method should have @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
in order to commit in a separate transaction, otherwise it will join the onMessage
transaction (which default to REQUIRED
) and bypass your exception handling code, beign committed after the successful execution of onMessage
What I woud do is:
Move the save
method to a new Stateless session bean:
@Stateless
public class AnalyzerResultSaver
{
@PersistenceContext(unitName="QIS")
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
Some s = em.find(Some.class, somepk);
s.setSomeField("newvalue");
// SQL Exception happens after leaving this method
}
}
Inject this bean in your MDB:
public class SaveAdMessageDrivenBean implements MessageListener {
@Inject
private AnalyzerResultSaver saver;
@Resource
private MessageDrivenContext mdc;
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage obj = (ObjectMessage)message;
AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
saver.save(alyzres);
}
} catch (Throwable e) {
mdc.setRollbackOnly();
log.log(Level.SEVERE, e);
}
}
}
Another tip: in this code the message poisoning still exists. Now it derives from the line invoking mdc.setRollbackOnly();
.
I'd suggest here to log the exception and transfer the message to a poison queue, thus preventing the container to resubmit the message ad infinitum.
UPDATE:
A 'poison queue' or 'error queue' is simply a mean to guarantee that your (hopefully recoverable) discarded messages will not be completely lost. It is used heavily in integration scenarios, where the correctness of the message data is not guaranteed.
Setting up a poison queue implies defining a destination queue or topic and redeliver the 'bad' messages to this destination.
Periodically, an operator should inspect this queue (via a dedicated application) and either modify the messages and resubmit to the 'good' queue, or discard the message and ask for a resumbit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With