Is it possible to catch an exception in a CMT(Container Managed Transaction) stateless bean?
The code below wont catch any exeption when I tried it. If I use BMT(Bean Managed Transaction), I can catch the exception. But I want to remain with CMT.
#Path("books")
public class BookResource
{
#EJB
private BooksFacade book_facade;
private Books local_book;
#POST
#Consumes({"application/xml", "application/json"})
public Response create(Books entity)
{
try
{
book_facade.create(entity);
} catch (RuntimeException ex)
{
System.out.println("Caught database exception");
}
return Response.status(Response.Status.CREATED).build();
}
public class TXCatcher
{
//#Resource
//UserTransaction tx;
private final static Logger LOG = Logger.getLogger(TXCatcher.class.getName());
#AroundInvoke
public Object beginAndCommit(InvocationContext ic) throws Exception
{
//ic.proceed();
System.out.println("Invoking method: " + ic.getMethod());
try
{
//tx.begin();
Object retVal = ic.proceed();
//tx.commit();
return retVal;
}catch (RollbackException e)
{
LOG.log(Level.SEVERE, "-----------------Caught roolback(in interceptor): {0}", e.getCause());
System.out.println("Invoking method: " + ic.getMethod());
throw new CustomEx("Database error");
}catch (RuntimeException e)
{
LOG.log(Level.SEVERE, "-----------------Caught runtime (in interceptor): {0}", e.getCause());
System.out.println("Invoking method: " + ic.getMethod());
//tx.rollback();
throw new CustomEx("Database error",e.getCause());
//throw new CustomEx("Database error");
}
//return ic.proceed();
}
}
It depends what kind of problem you're trying to catch. You could try an explicit EntiyManager.flush, but depending on your data source isolation level, some errors cannot be caught until transaction commit, and there is no mechanism for catching transaction commit errors for a CMT. If that's the case, your only option is to use BMT (even though you said you don't want to). The only suggestion that might make that more palatable would be to write an EJB interceptor that behaves similarly to CMT (that is, inject UserTransaction into the interceptor, and begin/commit/rollback in the #AroundInvoke).
By placing the following above my function in my BooksFacade class create function, the CMT created a 2nd transaction within the first transaction. When the exception was thrown from the 2nd transaction, my BookResource class create method could catch it. No need for BMT.
#Overide
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void create(Books entity)
{
super.create(entity);
}
I noted that the annotation only works when placed on the individual methods, by placing it on the class itself wont make a difference.
Related
I recently got into problem with play framework 2 #Transactional. Based on my tests, in the case of an exception, a transactional method would only rollback if the exception is unchecked (no catch block).
Here's my controller:
#Transactional
public Result myController(){
ObjectNode result = Json.newObject();
try{
JsonNode json = request().body().asJson();
someFunction(json);
//doing some stuff using the json object inside someFunction
//which I may intentionally throw an exception
//based on some logic from within
//(using "throw new RuntimeException()")
result.put("success", true);
return ok(Json.toJson(result));
}catch(Exception e){
result.put("success", false);
result.put("msg", e.getMessage());
return internalServerError(Json.toJson(result));
}
}
I want my controller to always return a JSON in response. But this comes at the expense of not having a database rollback when I throw an exception in my code. I know that in spring you can add this to #Transactional annotation but I'm using play.db.jpa.Transactional. Is there any way I can do a rollback in my catch block without using spring?
The #Transactional annotation basically wraps your action's code in a call to DefaultJpaApi.withTransaction. If you look at the source you can see how this method handles the transaction.
Since you want to catch the exception, but still want to use the withTransaction behaviour, you could try removing the #Transactional annotation and calling withTransaction yourself within the action.
E.g.
class MyController {
private final JPAApi jpa;
#Inject
public MyController(JPAApi jpa) {
this.jpa = jpa;
}
public myAction() {
ObjectNode result = Json.newObject();
try {
JsonNode json = request().body().asJson();
// Calls someFunction inside a transaction.
// If there's an exception, rolls back transaction
// and rethrows.
jpa.withTransaction(() -> someFunction(json));
// Transaction has been committed.
result.put("success", true);
return ok(Json.toJson(result));
} catch(Exception e) {
// Transaction has been rolled back.
result.put("success", false);
result.put("msg", e.getMessage());
return internalServerError(Json.toJson(result));
}
}
}
I have a scenario where I call three #Transactional #Async methods. Everything works fine except all three methods have their own transaction context. I want to execute them in calling method's transaction context.
My Calling method is like this:
#Transactional
public void execute(BillingRequestDto requestDto) {
try {
LOGGER.info("Start Processing Request : {}", requestDto.getId());
List<Future<?>> futures = new ArrayList<>();
futures.add(inboundProcessingService.execute(requestDto));
futures.add(orderProcessingService.execute(requestDto));
futures.add(waybillProcessingService.execute(requestDto));
futures.stream().parallel().forEach(future -> {
try {
future.get();
} catch (Exception e) {
futures.forEach(future1 -> future1.cancel(true));
throw new FBMException(e);
}
});
requestDto.setStatus(RequestStatus.SUCCESS.name());
requestDto.setCompletedAt(new Date());
LOGGER.info("Done Processing Request : {}", requestDto.getId());
} catch (Exception e) {
requestDto.setStatus(RequestStatus.FAIL.name());
requestDto.setCompletedAt(new Date());
throw new FBMException(e);
}
}
And all called methods are annotated with #Async and #Transactional.
#Transactional
#Async
public Future<Void> execute(BillingRequestDto requestDto) {
LOGGER.info("Start Waybill Processing {}", requestDto.getId());
long count = waybillRepository.deleteByClientNameAndMonth(requestDto.getClientName(), requestDto.getMonth());
LOGGER.info("Deleted {} Records for Request {} ", count, requestDto.getId());
try (InputStream inputStream = loadCsvAsInputStream(requestDto)) {
startBilling(requestDto, inputStream);
} catch (IOException e) {
LOGGER.error("Error while processing");
throw new FBMException(e);
}
LOGGER.info("Done Waybill Processing {}", requestDto.getId());
return null;
}
Implementation for all three methods is more or less same.
Now if there is a failure in any of these methods then transaction in rolled-back for that method only.
My requirement is to run all three methods in calling methods transaction context so any exception in one method will rollback all three methods.
This scenario works well if I disable #Async. There are time taking methods so I want them to run in parallel.
Please suggest any solution for this.
I guess you should use spring TransactionTemplate for programmatic control.
The main thread should perform control, if any thread throws exception you should notify the others thread that they should be rollbacked.
Say, each "transactional" thread after execution should wait(), in case of no exception just perform notifyAll() and in your thread do transaction commit, in case of exception you should call Thread.interrupt() and do rollback.
I think that yours #Async method can throw checked exception
#Transactional
#Async
public Future<Void> execute(BillingRequestDto requestDto) throw RollBackParentTransactionException {
...
}
and the caller can be annotated with:
#Transactional(rollbackFor = RollBackParentTransactionException.class)
public void execute(BillingRequestDto requestDto) { ... }
that should work.
I have a problem when I tried to test my class with the database. I am not using hibernate so I cannot rollback database. When exception occurs the database operation stops the test for the exception passes but it fails other test I wrote. How can I prevent this?
#RunWith(SpringJunit4ClassRunne.class)
#ContextConfiguration("test.xml")
public class MachineDaoTest{
#Autowired
private DataSource ds;
#Before
public void setUp(){...}
#Test
public void testFindVersion(){
....
}
#Test
public void testException() {
try {
someMethod.getVersion("xxyyzz");
fail("Expected DB Exception");
} catch (Exception e) {
assertTrue(e instanceof DbException);
}
Now in xml file I have included two sql file. One create tables and set values and another one that drop tables and cause exception. Since the database was autowired how am I able to rollback or make both test pass?
In an actual java file to be tested ...
...
try{
result = someMethod.getVersion("x");
if(result==null) {
throw new DBexception("result should return");
} catch (DBexception e) {
e.getMessage();
}
...
I am trying to use a Transactionnal RabbitMQ channel with Spring-AMQP but I want to actually swallow Exceptions to log them and being able to recover them.
Using channelTransacted=true forces the Channel to also join the current transactionManager (Hibernate in my case) and that results in the commit Exception being rethrown out of the #Transactionnal boundaries, resulting in a failure at upper level without being able to catch it and log it.
I also tried to manually attach the publish to the Transaction so it gets executed only after commit succeeded :
public void publishFailSafeAfterSuccessfulTransaction(final String routingKey, final String message) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
try {
rabbitTemplate.convertAndSend(routingKey, message);
} catch (Exception exception) {
logger.error("Error while publishing message to RabbitMQ ");
}
}
});
used in that way :
Entity entity = save(entity);
publishFailSafeAfterSuccessfulTransaction("routingkey", "Entity was updated");
but in that case I cannot use the channelTransacted=true because it will nest the registeringSynchronization inside another registeringSynchronization and fail to be called at all...
Is there a way this can be achieved ?
UPDATE : Ideally I would want to override the RabbitResourceSynchronization that is used in the ConnectionFactoryUtils class, but it is a private class without factory instanciated with
TransactionSynchronizationManager.registerSynchronization(new RabbitResourceSynchronization(resourceHolder, connectionFactory, synched));
The solution I implemented was to do the publishing inside a new transaction after the commit of the main transaction.
The first call :
Entity entity = save(entity);
publishFailSafeAfterSuccessfulTransaction("routingkey", "Entity was updated");
This method register to do the publishing after the main transaction committed.
public void publishFailSafeAfterSuccessfulTransaction(final String routingKey, final String event) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
try {
publishFailSafe(routingKey, event);
} catch (Exception exception) {
//Do some recovering
}
}
});
}
After the main transaction committed, this one will do the publishing. As the channel is transacted, it will commit the message at the commit of that new transaction and fail only that one and errors will be caught in the previous method.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void publishFailSafe(String routingKey, String event) {
try {
rabbitTemplate.convertAndSend(routingKey.getRoutingKey(), event);
} catch (Exception exception) {
//Do some recovering
}
}
I am implementing an JEE7 web application. During my work i have found a problem with handling my custom exceptions.
I edited my account's property to have a non-unique login field. Then i invoked the AccountEditBean#editAccount() to run the editing process. When the process comes to AccountFacade#edit() i can see (in debug) that PersistenceException is caught and my custom NonUniqueException is thrown. The problem is, the exception is not propagated out of the facade class and it is not handled in AccountEditBean. Instead of that TransactionalException occurs right after throw:
WARNING: EJB5184:A system exception occurred during an invocation on
EJB ADMEndpoint, method: public void
pl.rozart.greatidea.adm.endpoints.ADMEndpoint.editAccount(pl.rozart.greatidea.entities.Account)
throws pl.rozart.greatidea.exceptions.BaseApplicationException
WARNING: javax.transaction.TransactionalException: Managed bean with
Transactional annotation and TxType of REQUIRES_NEW encountered
exception during commit javax.transaction.RollbackException:
Transaction marked for rollback.
Additional information:
NonUniqueException extends BaseApplicationException , which is marked as #ApplicationException(rollback=true).
Here's the code for the edit process:
AccountEditBean:
#Named(value = "accountEditBean")
#ViewScoped
public class AccountEditBean extends UtilityBean implements Serializable {
#Inject
ADMEndpointLocal admEndpoint;
private Account account;
public void editAccount() {
try {
admEndpoint.editAccount(this.account);
Messages.addInfo(ACCOUNT_DETAILS_FORM, KEY_CHANGES_SUCCESS);
} catch (NonUniqueException e) {
Messages.addError(ACCOUNT_DETAILS_FORM, e.getMessage());
} catch (BaseApplicationException e) {
Messages.addFatal(ACCOUNT_DETAILS_FORM, e.getMessage());
}
}
}
ADMEndpoint:
#Stateful
#Transactional(Transactional.TxType.REQUIRES_NEW)
#TransactionTracker
public class ADMEndpoint extends LoggingStateBean implements ADMEndpointLocal, SessionSynchronization {
#EJB(name = "ADMAccountFacade")
private AccountFacadeLocal accountFacade;
private Account account;
#Override
public void editAccount(Account account) throws BaseApplicationException {
this.account.setLogin(account.getLogin());
this.account.setEmail(account.getEmail());
accountFacade.edit(this.account);
}
}
ADMAccountFacade:
#Stateless(name = "ADMAccountFacade")
#Transactional(Transactional.TxType.MANDATORY)
#TransactionTracker
public class AccountFacade extends AbstractFacade<Account> implements AccountFacadeLocal {
#PersistenceContext(unitName = "myPU")
private EntityManager em;
#Override
public void edit(Account account) throws BaseApplicationException {
try {
em.merge(account);
em.flush();
} catch (PersistenceException e){
if(e.getMessage().contains(Account.CONSTRAINT_ACCOUNT_LOGIN_UNIQUE)){
throw new NonUniqueException(NonUniqueException.MSG_NON_UNIQUE_ACCOUNT_LOGIN, e);
}else{
throw new BaseDatabaseException(BaseDatabaseException.MSG_GENERAL_DATABASE_ERROR, e);
}
}
}
}
Do you know what could be the cause of the problem? It occurs in every of my facades, with all the custom exceptions.
I think you should change #Transactional to #TransactionAttribute because EJBs annotated with that. #Transactional is put on managedbean in java 7 not in EJBs...
I copied my comment here because i do not have enough points to squander :)
You are throwing an exception from a method whose invocation will be intercepted at runtime and additional logic wrapped around it:
transaction management;
exception handling.
Your exception cannot transparently jump over that logic, and the specification (probably) says a TransactionalException will be thrown, wrapping your original exception (again, probably---I am not that intimate with the details).