I am currently working on an enterprise solution which is deployed as an EAR module in weblogic12c. I need to write a business logic where there will be entity manager methods which perform some updates on database using EntityManager interface with EclipseLink and I need to have an Interceptor around these methods which will intercept the method calls and push some data to JMS queue if the target method does not fail.
Since interceptors live inside same transaction and security context as the intercepted method, so if one fails, it causes the other to fail, i.e. if done by throwing an EJBException. My problem is regarding those transaction rollback exception which you cannot catch before the transaction is actually committed. So even if the intercepted method gets a RollbackException, the interceptor is not immediately terminated since it's running in the same transaction model, so it cannot catch those rollback exceptions. Eventually it is rolled back too, but in the meantime, data gets pushed to JMS queue which is not really desirable.
I also cannot take the JMS queue part out of the interceptor, because, as a software requirement, if the module fails to push data to jms, the transaction should fail and rollback too. So what I am trying to achieve here is, if the intercepted method fails, the transaction will fail too and there should not be any execution in the interceptor beyond context.proceed() method call.
Now, let's say I have a business method:
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Interceptors(TestInterceptor.class)
@Override
public TestBean update(TestBean testBean) {
try {
TestEntity testEntity = em.createNamedQuery("TestEntity.findById", TestEntity.class)
.setParameter("id", testBean.getId())
.getSingleResult();
// only some bean conversions before merging
testEntity.setName(testBean.getName());
testEntity.setPhone(testBean.getPhone());
testEntity.setImage(testBean.getImage());
em.merge(testEntity);
em.flush();
testBean.setVersion(testEntity.getVersion());
logger.debug("Update successful");
return testBean;
} catch (Exception ex) {
logger.debug("update::exception: ", ex);
throw ex;
}
}
And a web service method:
@MethodPrivilege(MethodRole.GENERIC)
@WebMethod
@Override
public String testDataCRUD(@WebParam(name = "auth") AuthRequest authRequest,
@WebParam(name = "id") Long id,
@WebParam(name = "name") String name,
@WebParam(name = "phone") String phone,
@WebParam(name = "image") byte[] image,
@WebParam(name = "action") Action action) {
...
// build testBean from received parameters
...
try {
testManager.update(testBean);
return "SUCCESS";
} catch(Exception ex) {
return "FAILED -- UPDATE Error: " + ex.getMessage();
}
}
And finally here is the interceptor method:
@AroundInvoke
private Object around(InvocationContext ctx) throws Exception {
Object testParam = null;
TestBean testBean = null;
logger.debug("Test intercepted");
try {
testParam = ctx.proceed();
} catch(Exception ex) {
logger.debug("Interceptor exception ", ex);
throw new EJBException("Target method raised an exception");
}
// do some bean conversion here
...
...
// construct the message object here
boolean success = jmsNotifier.send(message, false);
if(!success) {
throw new Exception("JMS failed.");
}
logger.debug("Successfully sent to jms");
return testParam;
}
Now if the business method gets a RollbackException, the service layer can catch it because the business method initiated a new transaction. But so far I am unable to catch it in the try catch block around ctx.proceed(); as a result, the code goes on and pushes data to jms queue and then gets the exception upon completion of the transaction. How can I resolve this issue? Note: sorry for my bad English, please let me know if I need to clarify some part of it any farther.
Update: (After Display Name is missing's comment)
To describe why I am doing this - say for example I am adding an additional jar package to an existing ejb project, the service layer and the business / entity manager layer is from an existing application. Now, the additional ejb jar I am adding performs some additional stuffs around the target business methods and there can be many methods like this. So any of my colleagues can grab the jar, add it to their projects and I need a way so that it will require them a minimal amount of change in their business or entity methods. So far what I have tried is, all they need to do is to add an Interceptor class which then can use the functionality of the my plugin library. That way only change needed in the business layer is to add an extra line stating the @Interceptor annotation and existing code base not modified by a great extent.
Also tried adding em.flush() but it does not help in the described circumstances. Any help is much appreciated.