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).
Related
I am working on a spring boot app where I am using transactional and its not rolling back its changes when I throw a exception:
My method:
private BtoBWalletTransactionResponseModel doWalletOperation(BtoBWalletTransactionTypes transactionType, BtoBWalletTransactionRequestModel transactionRequest) {
// DB Operation
BtoBWalletTransaction savedTransaction = commonTransactionalService.finishWalletTransaction(userWallet, btoBWalletTransaction);
log.info("wallet {} txn of amount {} for user {}",transactionType.name(),txnAmount,userId);
// throwing a exception to rollback
throw new RuntimeException("Time to Rollback");
} catch(Exception e){
log.error(e.getMessage());
log.error("error while doing wallet operations for user {}",userId);
throw new WalletException(e.getMessage());
}
}
My common TransactionalService Interface:
public interface CommonTransactionalService {
BtoBWalletTransaction finishWalletTransaction(BtoBUserWallet userWallet,BtoBWalletTransaction btoBWalletTransaction);
}
My Interface Impl:
import javax.transaction.Transactional;
#Service
public class CommonTransactionalServiceImpl implements CommonTransactionalService {
#Autowired
private BtoBWalletTransactionRepo btoBWalletTransactionRepo;
#Transactional
#Override
public BtoBWalletTransaction finishWalletTransaction(BtoBUserWallet userWallet, BtoBWalletTransaction walletTransaction) {
BtoBWalletTransaction savedTransaction = btoBWalletTransactionRepo.save(walletTransaction);
btoBUserWalletRepo.save(userWallet);
return savedTransaction;
}
}
Now even when I am sending a RuntimeException the DB record is not getting rolled back.
Can someone help? stuck since hours here.
Transactional is scoped, if you anotate a method (or class) as #Transactional all the methods this class calls wil also be transactional. and if within this transaction an exception occurs things wil be rolled back.
If however like in you example a non-transactional method calls a transactional one and after that call throws an exception the previous transactioned function wil not be rolled back as it's outside of the transactions scope.
This question already has answers here:
Letting the presentation layer (JSF) handle business exceptions from service layer (EJB)
(1 answer)
Handling service layer exception in Java EE frontend method
(3 answers)
Closed 5 years ago.
Recently, I've decided to use EJB in my project. Basically, are Stateless EJBs in order to make the business layer and persist data. I'm also using container managed transactions (CMT) and everything works fine.
I tried to handle the exceptions in EJB service methods in order to get the more specific exceptions and give specific errors messages to the client.
#Stateless
public class EJBService {
#PersistenceContext(unitName="PU")
private EntityManager em;
public void save(Animal a) throws AppException
{
try
{
em.persist(e);
}
catch(ConstraintViolationException ex)
{
throw new AppException("message");
}
catch(Exception ex)
{
throw new AppException("message 1");
}
}
}
The AppException extends Exception and is marked with the annotation #ApplicationException(rollback=true)
However, I can't handle the exception before the method commit the transaction. If something goes wrong the transactions are not commited (OK), but I don't have the specific exception, only javax.ejb.EJBTransactionRolledbackException: Transaction rolled back. I could use the flush method after try to persist or merge, but it isn't seems right.
So, I've decided don't try to handle the exceptions in EJB service layer leaving my methods like this:
#Stateless
public class EJBService {
#PersistenceContext(unitName="PU")
private EntityManager em;
public void save(Animal a)
{
em.persist(a);
}
and the client (A managed bean, for example), like this:
#Named
#RequestScoped
public class Bean {
private Animal animal;
#Inject
private EJBService service;
public void save() {
try
{
service.save(animal);
}
catch(Exception e)
{
//Unwrapping the exception in order to pass the
//appropriate message
}
}
}
In order to show the right messages, the method who invoked the ejb service must to handle the exception by itself, unwrapping the exception.
The question is: Am I right ? If I'm not, what is the best way to handle exceptions ?
Given this example code:
public class MyServiceImpl implements MyService {
#Transactional
public void myTransactionalMethod() {
List<Item> itemList = itemService.findItems();
for (Item anItem : itemList) {
try {
processItem(anItem);
catch (Exception e) {
// dont rollback here
// rollback just one item
}
}
}
#Transactional
public void processItem(Item anItem) {
anItem.setSomething(new Something);
anItem.applyBehaviour();
itemService.save(anItem);
}
}
Here is what I want to achieve:
Only processItem(anItem); should rollback if exception occurs inside it.
If exception occurs, myTransactionalMethod should continue, that means the for-each should end.
If exception occurs inside myTransactionalMethod but not in processItem(anItem), myTransactionalMethod should rollback completely.
Is there a solution that doesn't involve managing transactions manually (without annotations)?.
Edit: I was thinking of using #Transactional(PROPAGATION=REQUIRES_NEW), don't know if it will work within the same bean though.
This is a common misunderstanding. Spring Transactions are implemented through proxies. Proxies are a wrapper around your class. You are accessing the processItem method from the same class, i.e. you don't go through the proxy, so you don't get any transactions. I explained the mechanism in this answer some years ago.
Solution: you need two separate Spring beans if you want nested transactions, both of them must be proxied by #Transactional.
It looks like a case for NESTED transaction. NESTED transaction starts a subtransaction with in the outer transaction with savepoint, allowing it rollback to that savepoint. Since it is a nested transactions they committed at the end of outer transation.
public class MyServiceImpl implements MyService {
#Transactional
public void myTransactionalMethod() {
List<Item> itemList = itemService.findItems();
for (Item anItem : itemList) {
try {
// If you want to call this method directly configure your transaction use to aspectJ for transaction handling or refactor the code. Refer - [http://stackoverflow.com/questions/3423972/spring-transaction-method-call-by-the-method-within-the-same-class-does-not-wo][1]
processItem(anItem);
catch (Exception e) {
// dont rollback here
// rollback just one item
}
}
}
#Transactional(PROPAGATION = PROPAGATION.NESTED)
// Throw some runtime exception to rollback or some checkedException with rollbackFor attribute set in the above annotation
public void processItem(Item anItem) {
anItem.setSomething(new Something);
anItem.applyBehaviour();
itemService.save(anItem);
}
}
Note that, I have not yet tried this below code see if that helps. You might have to tweak it, if needed. In fact I would love to give this code a try myself sometime soon.
I've created my own exception class:
public class ValidationException extends RuntimeException { ... }
I've declared it in EJB interface method:
public interface MyApi {
void save(MyDTO dto) throws ValidationException;
}
Now I've used it in the implementation:
#Stateless
#Local(MyApi.class)
public class MyService implements MyApi {
public void save(MyDTO dto) throws ValidationException {
...
throw ValidationException(errorMessages);
}
}
However, when I call that method:
#Path("/my")
#Stateless
public class MyChannel {
#Inject private MyApi myApi;
public void save(MyDTO dto) {
try{
myApi.save(dto);
} catch (ValidationException ex) {
// do sth with the exception
}
}
}
Instead of expected ValidationException, the EJBException is thrown with the following message:
0000167f BusinessExcep E CNTR0020E: EJB threw an unexpected (non-declared) exception during invocation of method "save" on bean
It surprised me, because the exception is declared in the interface and in the implementation. How else can I declare the exception, in order to be able to use it to communicate errors to the caller?
The whole mayhem happens on the WebSphere 8.5. I'm using EJB 3.0 and WebSphere libraries. The channel is JSON REST channel in the WAR module, which is wrapped in EAR module.
I believe the root of your problem lies in choosing to have your custom ValidationException extend RuntimeException. Within Java, RuntimeException or any subclass of RuntimeException does not have to be declared using a throws clause on a method signature. The intent of RuntimeException is that it is generally used in unrecoverable bug scenarios that are the result of something done incorrectly by the method caller, such as attempting to traverse beyond the end of an array (ArrayIndexOutOfBoundsException) or passing an invalid parameter (IllegalArgumentException).
Given that you would like to make your ValidationException part of the method signature and thereby require the calling client to handle the exception, I suggest the following change:
//Modify your exception so that it
//subclasses Exception (not RuntimeException):
public class ValidationException extends Exception { ... }
You will not have to modify the MyService interface, because the save method already declares that it throws the exception. But this small change will shift the way Java handles ValidationException so that when the exception is thrown, it will behave in the way you expect (and without the extraneous noise about an "undeclared" exception).
Use the #ApplicationException annotation. For example
#ApplicationException
public class ValidationException extends RuntimeException {
private static final long serialVersionUID = 7797343376699439504L;
}
You can use it with RuntimeException so you don't have to use throws declarations.
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.