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.
}
}
You got a bad case of message poisoning...
The main issues I see are that:
you are calling directly the save() method in your onMessage(): this means thet the container has no way to inject the proper transaction handling proxy around the save method
in any case the save() 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.
I believe that the code that you have posted is mostly OK.
Your use of
#TransactionAttribute(TransactionAttributeType.REQUIRED)
is completely ignored because this (and most other) annotations can only be applied to business methods (including onMessage). That doesn't matter though because your onMessage method gets an implicit one for free.
This leads to the fact that message handling is transactional in a Java EE container. If the transaction fails for any reason the container is required to try and deliver the message again.
Now, your code is catching the exception from the save method, which is good. But then you're explicitly marking the transaction for rollback. This has the effect of telling the container that message delivery failed and that it should try again.
Therefore, if you remove:
mdc.setRollbackOnly();
the container will stop trying to redeliver the message.
If I'm not mistaken, you're letting the container handle the transactions. This way, the entity manager will queue the operations that will be flushed after the method finishes, that's why you're having exceptions after the method is finished.
Using em.flush() directly as a final step in the method will execute all the related queries of the transaction, throwing the exceptions there instead of being thrown later when the flush() is made by the container while commiting the transaction.
Related
I have a question about how to handle EJBExceptions in some special cases.
General situation
In our application an EJB (3.0) DataAccessObjects(DAO) are responsible for all database access, these are used in other parts of the application.
Exceptions in the database layer are expected, for instance because of Optimistic Locking or Database constraint violations. The exceptions often thrown outside of the DOA class, because they occur on commit of the automatic JTA transaction. The calling class then receives this exception wrapped in an EJBException.
In most cases ignoring or logging and rethrowing the EJBException is best, as our JMS will trigger automatic retries for MessageDrivenBeans. In two cases, we don't want the exception to be propaged, as they have unwanted side effects.
Handling in JSF
In our JSF website we use the following pattern to display user friendly messages:
#ManagedBean
#ViewScoped
public class MyDataController {
#EJB
private MyDataDao myDataDao ;
public void addData(){
FacesMessage msg;
try {
Data data = new Data();
// Data gets filled
myDataDao.addData(data);
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Succes",
data.getName());
}
catch (EJBException e) {
LOGGER.warn("Failed to add data"newCompanyName, e);
if (ExceptionUtils.getRootCause(e) instanceof SQLIntegrityConstraintViolationException) {
msg = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Failed",
data.getName());
}
else {
msg = new FacesMessage(FacesMessage.SEVERITY_FATAL,
"Failed to add data for unknown reason", data.getName());
}
}
}
}
Handling in Schedules tasks
In a related case, we call the database from a timed task (created using #Schedule). However this task is destroyed when (two consecutive?) exceptions occur while running it (at least in Weblogic). For us it is very important that this task keeps running even if exceptions during handling occur.
We have achieved this by catching and logging all EJBExceptions, as explained in this answer and in this answer. (Actually in our case we decided to catch all exceptions).
Problem
The above solutions mostly work as intended. However, we recently found that Errors are wrapped in an EJBException as well. For instance an OutOfMemoryError. In this case it caused the error to be swallowed, disabling the normal mechanism in Weblogic that would restart the application in case of an OutOfMemoryError.
Effectively this has downgraded Errors to Exceptions.
Question
The only solution I have is to check the recursively check the exception and its causes for instances of type Error, and if that is the case, rethrow the EJBException.
What is the correct way to handle the EJB exceptions in these situations?
I have an EJB with container-managed transactions. I have a method in it (used as a REST call) that calls something from another EJB I've injected using JNDI (not sure if that matters) and it returns an Exception that extends RuntimeException (so it causes a transaction rollback), which translates as a 404 response through an ExceptionMapper.
I want that exception to be what returns from my REST call and I don't mind it being in the logs at all, but I do not want my log to be spammed with the EJBExceptionRolledBackException stacktrace that it causes (the stacktrace gets printed three times for some reason). I believe two out of these three stacktraces get logged before the server even gets back to the final method for the REST call.
Either way, as long as I figure out how to suppress one of these logging actions I'll figure out a way to stop all three. Does anyone have an idea how to suppress this kind of logging?
As it said in the EJB Specification every SystemException must be logged by the container implementation. You can try to catch it or mark as ApplicationException but if you mark it it won't rollback the transaction. I suggest this:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class MyBean {
#Resource
private UserTransaction tx;
public void myMethod() throws MyApplicationException {
try {
tx.begin();
// call needed methods
tx.commit();
} catch(Exception e) {
// silently rollback;
// exceptions from `UserTransaction` omitted for readability
tx.rollback();
throw new MyApplicationException(e);
}
}
}
Now in your client code of that EJB you can react to MyApplicationException and return whatever you want or log it or don't. By using container managed transactions will ensure that errors are logged by specification (and they are wrapped to another exceptions as bean instances are being destroyed). Also you can mark transaction as rollback-only. Be sure to use this carefully. If you don't want logs from container you need to control all of your flow by yourself.
I have a class:
#Service
#Transactional
class MyService{
#Autowired MyTableRepository repository;
#Autowired FacebookMessageSender sender;
public void updateTableAndSendMessage(MyTable m){
m.setProcessed(1);
repository.save(m);
sender.sendMessageToFacebook(m);
}
}
Somewhere in the code:
List<MyTable> list=repository.findByProcessed(0);
for(MyTable m:list){
myService.process(m);
}
So, I have the following:
In the quartz job I retrieve all records with processed flag set to 0. Then I pass it to service, then processed flag becomes 1 and system sends message to Facebook messenger (wherever). But, if there's a sql exception occurs, before or after 'process' method execution, transaction will be rolled back and flag will be still 0. So Facebook message will be sent on next job launch, and again and again. I tried to break it down to 2 methods, one saves flag other sends message. But then what if message was not sent and I will have to rollback the transaction? So it's like deadlock. I need prevent FB message from being send on other exception and rollback DB changes on FB sending failure. How to do that in spring-data, afaik transaction will be committed after method ends. Thanks
The following code structure should work:
Service class
#Service
class TaskService {
#Autowired TaskRepository repository;
#Autowired FacebookMessageSender sender;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendTaskNotification(Task task){
try {
task.setProcessed(1);
repository.save(task);
sender.sendMessageToFacebook(task);
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
Caller class
#Component
class TaskNotificationJob {
#Autowired TaskService service;
#Scheduled
#Transactional
public void sendTaskNotifications() {
for(Task task : repository.findByProcessed(0)) {
try {
service.sendTaskNotification(task);
}
catch (RuntimeException e) {
// Log the error, but don't rethrow the exception.
}
}
}
}
Explanation
a. Ensure that a Facebook message is not attempted if task cannot be saved
If a single task cannot be saved, an exception will be raised and the code will skip the part that attempts to send a Facebook message for the task. Therefore, this requirement is automatically met due to the way the code is structured.
b. Ensure that an error while sending a Facebook message rolls back the task
The lines catch(Throwable t) { throw new RuntimeException(t); } ensure that if any exception (including one that derives from Exception instead of RuntimeException) is thrown while either saving a task or sending a Facebook message for it, it gets re-thrown as a RuntimeException. This in turn will ensure that the #Transactional annotation surrounding the method will roll the database transaction back.
c. Ensure that an error while processing a task does not affect other tasks
Each task is processed in its own transaction (#Transactional(propagation = Propagation.REQUIRES_NEW)) so anything that gets committed before an error occurs, stays committed.
When an error occurs, the calling code catches it (catch (RuntimeException e)) so that the error does not bubble up to the transaction handler for the caller and caller is allowed to attempt and process remaining tasks, as required. Not having this catch clause would mean that the first RuntimeException raised in the service will also bubble up to the caller and will terminate the whole operation immediately, potentially leaving unprocessed tasks. It is important not to re-throw the caught exception at this point because re-throwing it would bubble it up and hence defeat the purpose of having the catch clause in the first place.
Application declares interface for sending notifications with single method with ListenableFuture<> return type.
For mail service I don't see exceptions in case of misconfiguration (for example if SMTP server is down or host is not resolved) in my batch job.
Debugging shows that on:
MailNotificationService mailService = applicationContext.getBean(MailNotificationService.class);
I receive proxy that on method invocation:
mailService.send(mime);
resolved with stack:
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.invoke(AsyncExecutionInterceptor.java:101)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
Documentation for invoke says:
* Intercept the given method invocation, submit the actual calling of the method to
* the correct task executor and return immediately to the caller.
After some steps I see new thread SimpleAsyncTaskExecutor-1 and service executed here.
It looks like service executed in separate thread and exception doesn't propagated to original thread (if it is meaningful at all).
Is it correct that for bean methods with ListenableFuture<> return type Spring execute them in separate thread?
NB My original issue in that I am blind in case of errors in notification service (traces are not logged). Mail service throws unchecked org.springframework.mail.MailException and the only way to find that out is to wrap .send() method by Exception with logging:
#Autowired
private JavaMailSender mailSender;
public void notify() {
try {
mailSender.send(mime);
} catch (Exception ex) {
log.warn("Can't deliver mail", ex);
}
}
As white predicted I have #Async annotation on service method and #EnableAsync on configuration class.
Exception handling described in section:
http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/htmlsingle/#scheduling-annotation-support-exception
In case of Future return type (ListenableFuture is subtype of it) exception propagated to future object and can be retrieved from exception ExecutionException.getCause() which occur on Future.get() method call.
In my case I ignore return type (don't call .get()). So exception was not captured and logged. As I wrote originally excpetion logging should be done in task itself in this case.
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutionException.html
I have an EJB interceptor and I follow the BCE pattern suggested by Adam Bien, that is, all EJB calls on the boundary starts and finish a transaction which means there is no nested EJB calls (there might be nested CDI injected Bean calls though, but those should be inside the same transaction started at the ejb Boundary).
So in those ejb Boundaries I have an interceptor and I want to intercept or know if after the method call of the EJB the transacction commited already? (that is, if a EntityManager was involved that the COMMIT sql call was sent to the DB and returned succeesfuly)
Will I get that info from inside an Interceptor ?
If not, how can I get notified of a transaction that sucessfully commited or failed ?
NOTE: Of course, if I am the client of the EJB and I am calling the method, after the method call I know what happened with the transaction, but I am interested in intercepting that BEFORE the client receives the response from the EJB.
#AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
Object proceed = null;
try {
proceed = ctx.proceed();
// is the transacction finished/commited already?
// is it still open ?
return proceed;
} catch (Exception e) {
throw e;
}
}
[UPDATE]: I accepted one good answer, but the thing is that THERE IS NO WAY in Java EE to receive an event of a transaction that HAS BEEN COMMITED. So regardless of the good answer, sadly there is no way to be notified in Java EE of a completed transaction, inside the server, of course, if you are the client caller, then you sure know the transaction commited or rolled back...
unless otherwise stated on the exception thrown, if an ejb method invocation throws an exception, it shall be rolled-back. Additionally, provided all calls to the DB were in the same transaction, they shall be deemed committed at the end of the transaction cycle.
In retrospect, all interceptors are invoked within the same transaction on which the ejb method it intercepts, was invoked (That's the reason the interceptor may decide in an event of an exception, to either roll-back or still commit the transaction).
Hence, you can know for sure, that the transaction completed successfully if within your interceptor call, after the proceed has been invoked and returned, there is no exception thrown, with a potential of transaction rollback.
So in your scenario:
#AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
Object proceed = null;
try {
proceed = ctx.proceed();
// is the transacction finished/commited already?
// The transaction is successful, but afaik, it is not yet committed, until this method returns successfully
// is it still open ? More or less. You can still grab the Ejbtransaction and commit it manually or rollback if some other conditions have not been met yet
return proceed;
} catch (Exception e) {
//If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
throw e;
}
}
Edit:
for you to actually commit the transaction in an interceptor, you will need to be running application-managed transaction, otherwise, it is prohibited by the EJB specs to call commit on a container managed transaction, you can of course call setOnrollback method of the EJBContext.
Edit
If you truly want to do some DB changes, i would recommend:
user ApplicationManaged transaction, from which you manually start
and commit the transaction within the interceptor
Use the concept of the observer, and listen for #Observes(AFTER_SUCCESS) event which will be invoked when the
transaction is successfully committed and complete, and hence you
can be guaranteed to do a db call, and the new updates will be
available.
If you can ignore the BCE pattern, and spin off a new transaction to do the update, so that after it returns successfully, you will be guaranteed of commit, and then continue normally
```
#Stateless
public class TransactionService {
#TransactionAttribute(REQUIRES_NEW)
public Object executeTransaction(final Callable<Object> task) {
return task.call();
}
}
#Interceptor
public class MyInterceptor {
#EJB
private TransactionService service;
#AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
Object proceed = null;
try {
proceed = service.executeTransactional(()->ctx.proceed());
//If you reach here, you will be guaranteed of commit and then you can do the elastic search update
return proceed;
} catch (Exception e) {
//If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
throw e;
}
}
}
Ok - the question is 4 years old now, but I think it makes still sense to give an answer.
You can for sure register a callback to get informed about the Transaction outcome. You simply use the registerSynchronization() API of javax.transaction.Transaction.
Transaction tx = ((TransactionManager) (new InitialContext()).lookup("java:/TransactionManager")).getTransaction();
tx.registerSynchronization(new Synchronization() {
public void beforeCompletion() {
// do stuff before completion
}
public void afterCompletion(int status) {
if (status == Status.STATUS_COMMITTED) {
// do something after successful commit }
}
});