EJB 2.0 CMP : exception scenario in multiple remove calls - java

I have EJB 2.0 legacy code. It has a session bean:
/**
* #ejb.bean
* name="Sample"
* type="Stateless"
* view-type="both"
* #ejb.transaction
* type="Required"
* #ejb.util
* generate="physical"
*/
public abstract class SampleEJB
implements SessionBean {
public void delete(Long id) {
EJBLocalObject local_o = getEjbLocalObject(id);
invokeDelete(local_o);
}
private void invokeDelete(EJBLocalObject local_o)
throws Exception
{
try
{
...
EJBLocalObject local_another_o = getEjbLocalObject(local_o.getAnotherId());
local_another_o.remove();
...
}
catch (Exception e)
{
// log exception
// throw new exception
}
try
{
...
local_o.remove();
...
}
catch (Exception e)
{
// log exception
// throw new exception
}
}
Sometimes due to issues in database, first remove call is successful. But second remove call fails and throws the exception.
This creates inconsistencies in database. Requirement is, all the remove calls should be successful. If one of the call fails, it should rollback the previous removes. How to handle such scenarios?
In case of BMT we can demarcate transactions for start, commit and rollback. But its CMT, so I am not sure how to handle this situation in EJB2.0. Please let me know what can be done.

#ejb.transaction * type="Required"
Assuming that this means the ejb transaction attribute configured is Requiered, the Container enforces that every call to delete() business method executes
within a transaction.
Therefore, to demarcate the transaction boundary is not a problem, you can be sure that both delete operations execute in the same transaction.
What you need is to mark the transaction for rollback if one delete operation fails.
The easier way to do this is that your business method throws a System exception.
} catch (Exception e) {
//log exception
throw new EJBException();
}
When the Container detects that a System exception (in ejb2.x this is exclusively an exception that extends from RuntimeException class) was thrown,
it automatically marks the transaction for rollback. However, when an Application Exception (an exception that extends from Exception) is thrown,
the Container doesn't change the transaction state.
In your case, it seems to be that delete() throws an Application Exception.
Other alternative is to explicitly marks the transaction for rollback using the SessionContext.setRollbackOnly() method.
//bean atribute
private SessionContext context;
//bean method call by the Container
public void setSessionContext(SessionContet ctx) {
context = ctx;
}
//your first delete code
try {
...
EJBLocalObject local_another_o = getEjbLocalObject(local_o.getAnotherId());
local_another_o.remove();
...
} catch (Exception e) {
context.setRollbackOnly();
//log exception
//throw new Exception
}
//idem for your second delete

Related

What is the best way to deal with exception in step 1 when step 2 is must to execute

In our application situation is, user has requested to update 2 fields say (A, B) for an account.
An account has multiple stores to which updated fields to be pushed. One of the store is marked as default store.
Field A has some validations to be done for default store (say limit on quantity.).
If validation fails I am throwing exception.
On success field value is added to store_space_table
Field B must be pushed to all stores. Pushing to store can throw exception when that store is down or unreachalbe.
Currently I have written this code in finally block.
I don't want to rollback the first operation on the exception in the second step. Rather I want to consolidate the exception of step 1 and step 2 and propagate that.
void validateFieldAndPushToStore(List<Field> inputFieldList, Account account) throws ServiceException {
List<Store> allStoresOfAccount = getAllStoresOfAccount(account);
Set<Store> storeListToPushData = new HashSet<>();
try{
if(ifFieldAUpdated(inputFieldList)) {
// get default store from list of stores of an account,
Store defaultStore = getDefaultStore(allStoresOfAccount)
// Validate space availability of A on default store, if validation is successful, then update data in store_space_table
validateSpaceOnDefaultStoreForFieldA(defaultStore);
storeListToPushData.add(defaultStore);
}
} finally {
if( ifFieldBUpdated(inputFieldList) ) {
storeListToPushData.addAll(allStoresOfAccount);
}
if( ! storeListToPushData.isEmpty()) {
// This operation reads fields A from DB (store_space_table), reads field B from field_tbl and push to stores.
pushUpdatesToStores(account, storeListToPushData);
}
}
}
As I read on multiple forums, such handling in finally is not correct/efficient. So I am looking for alternative or better approach to handle this situation.
The two updates should be wrapped in a transaction.
#Transaction is a nutshell.
Your service should be structured as follows.
#Transactional
public void validateFieldAndPushToStore(A a, B b) {
serviceA.validateAndPushA(a);
serviceB.validateAndPushB(b);
}
Where the implementations for serviceA and serviceB would be.
#Transactional
public void validateAndPushA(A a){
validate(a); // can throw validation exception from here
persist(a); // can throw persistence exception from here
}
#Transactional
public void validateAndPushB(B b){
validate(b); // can throw validation exception from here
persist(b); // can throw persistence exception from here
}
Please note the #Transactional on top of validateAndPushA and validateAndPushB.
The persist methods should be annotated with #Transactional as well.
If you would structure your code in this way, if any validation or persistence exception would occur, all database changes will be rolled back. This happens because #Transactional has a property called propagationLevel, which if left on the default value will execute any inner transaction (for example those of the persist operations) in a single outer transaction (i.e. validateAndPushA, validateAndPushB, validate and persist will all execute in the same transaction - so any exception thrown by those methods will result in the whole transaction being rollbacked).
#Transactional allows for a lot of fine tuning, such as for which exceptions the transaction should not be rolled back. Please consult the documentation for all the details.
Hope this helps!
As you have 2 operations which are supposed to get executed even if one of the fails and you also want to propagate the exception/error to above layer, following approach can be used-
void methodPerformingTwoOperations() throws Exception {
Exception firstOperationErrror = null;
Exception secondOperationError = null;
try {
performFirstOperation();
} catch(Exception e) {
firstOperationError = e;
}
try {
performSecondOperation();
} catch(Exception e) {
secondOperationError = e;
}
throwExceptionAsPerErrors(firstOperationError, secondOperationError);
}
void throwExceptionAsPerErrors(Exception firstOperationError, Exception secondOperationError) {
// depending upon exceptions compose and throw new exception from here
}
Update
Note that when using #Transactional always validate what parameters you are passing to this annotation. By default propagation property is set to REQUIRED which means all the transactions will run in the same transaction and if exception specified all will be reverted.
If you want to persist data from one operation even if other one fails then you can apply Transactional on the inner methods(but not on the main method). Please refer to the code from the answer by #alexrolea.
Capture the exception instead of using finally:
boolean failed = false;
try {
} catch (YourException ex) {
failed = true;
}
if (failed) {
}
If you want the exception to be propagated you can store it an a variable and then re-throw it.

Hibernate : throw exception in #PostUpdate

I have this code on an Spring/Hibernate webapp :
Entity :
#Entity
#Table(name = "ARTICLE")
#EntityListeners(ArticleEntityListener.class)
public class ArticleLocaliseBean extends EntiteTracee {
...
Listenner :
public class ArticleLocaliseEntityListener {
#PostUpdate
#PostPersist
private void checkQuantite(ArticleBean article) throws BusinessException {
if (article.getQuantiteStock() < 0) {
throw new BusinessException(exceptionMsg);
}
}
This code called after each update or persist on Article entity.
The problem is when an exception is thrown for negative quantity, hibernate convert the BusinessException on a RuntimeException and perform a rollback of the transaction.
java.lang.RuntimeException: xxx.exceptions.BusinessException: exceptionMsg.
at org.hibernate.ejb.event.ListenerCallback.invoke(ListenerCallback.java:53)
at org.hibernate.ejb.event.EntityCallbackHandler.callback(EntityCallbackHandler.java:94)
at org.hibernate.ejb.event.EntityCallbackHandler.postUpdate(EntityCallbackHandler.java:83)
at org.hibernate.ejb.event.EJB3PostUpdateEventListener.handlePostUpdate(EJB3PostUpdateEventListener.java:70)
at org.hibernate.ejb.event.EJB3PostUpdateEventListener.onPostUpdate(EJB3PostUpdateEventListener.java:62)
at org.hibernate.action.EntityUpdateAction.postUpdate(EntityUpdateAction.java:199)
How can I makes Hibernate throw a checked exception an not a Runtime ? I don't want a rollback of the transaction when an exception is thrown.
Thanks.
The exception is thrown upon call on an API method, such as persist(). These methods however are not declared to throw exceptions (there is no throws clause in their signature). Hibernate has to wrap any checked exception in a runtime exception to respect method's signature.
To avoid rolling back, you can catch the runtime exception and check for its cause.
try {
em.persist(entity);
} catch (RuntimeException e) {
if (e.getCause() instanceof BusinessException) {
// Fix the problem the way you want
} else {
throw e;
}
}

EjbConetxt setRollbackOnly vs ApplicationException rollback true

I have a method which throws an application exception with rollback true. I wonder if I have to explicitly call ejbContext.setRollbackOnly() or not?
The docs here says that we need not call the EjbContext's setRollbackOnly when the exception is marked as rollback=true.
In my case I catch a ApplicationException whose rollback attribute is set to true. Then I explicitly call ejbContext.setRollbackOnly() because I throw another exception in the catch block and this exception will be propagated to the client. Below is my code snippet
try {
....
} catch (XYZDirectoryException e) { // ApplicationException marked as rollback=true
ejbContext.setRollbackOnly(); // Is this necessary?
// log exception trace
throw new ServerInternalException("Something bad happened. Please try again or contact administrator. Check logs for more details"); // ApplicationException marked as rollback=false
}
An exception will not cause a rollback if it's thrown and caught inside a EJB transactional method. It has just to be thrown from a EJB transactional method in order to be caught by the transactional proxy around the EJB instance, otherwise, the container is completely unaware of the exception and thus won't rollback:
public void someEjbTransactionalMethod() {
// will cause a rollback
throw new XYZDirectoryException();
}
public void someOtheEjbTransactionalMethod() {
// will NOT cause a rollback
try {
throw new XYZDirectoryException();
}
catch (XYZDirectoryException) {
...
}
}
So, in your case, since the exception doesn't cross the boundary of any transactional EJB method, the container won't be aware of this exception, and won't rollback, unless you throw another "rollback=true" exception from the catch block, or explicitely mark the transaction as rollbackOnly.

UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

I have this scenario:
fetch (read and delete) a record from IncomingMessage table
read record content
insert something to some tables
if an error (any exception) occurred in steps 1-3, insert an error-record to OutgoingMessage table
otherwise, insert an success-record to OutgoingMessage table
So steps 1,2,3,4 should be in a transaction, or steps 1,2,3,5
My process starts from here (it is a scheduled task):
public class ReceiveMessagesJob implements ScheduledJob {
// ...
#Override
public void run() {
try {
processMessageMediator.processNextRegistrationMessage();
} catch (Exception e) {
e.printStackTrace();
}
}
// ...
}
My main function (processNextRegistrationMessage) in ProcessMessageMediator:
public class ProcessMessageMediatorImpl implements ProcessMessageMediator {
// ...
#Override
#Transactional
public void processNextRegistrationMessage() throws ProcessIncomingMessageException {
String refrenceId = null;
MessageTypeEnum registrationMessageType = MessageTypeEnum.REGISTRATION;
try {
String messageContent = incomingMessageService.fetchNextMessageContent(registrationMessageType);
if (messageContent == null) {
return;
}
IncomingXmlModel incomingXmlModel = incomingXmlDeserializer.fromXml(messageContent);
refrenceId = incomingXmlModel.getRefrenceId();
if (!StringUtil.hasText(refrenceId)) {
throw new ProcessIncomingMessageException(
"Can not proceed processing incoming-message. refrence-code field is null.");
}
sqlCommandHandlerService.persist(incomingXmlModel);
} catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
// send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,
ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
// send success outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId, ProcessResultStateEnum.SUCCEED.getCode());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
}
private void saveOutgoingMessage(OutgoingXmlModel outgoingXmlModel, MessageTypeEnum messageType)
throws ProcessIncomingMessageException {
String xml = outgoingXmlSerializer.toXml(outgoingXmlModel, messageType);
OutgoingMessageEntity entity = new OutgoingMessageEntity(messageType.getCode(), new Date());
try {
outgoingMessageService.save(entity, xml);
} catch (SaveOutgoingMessageException e) {
throw new ProcessIncomingMessageException("Can not proceed processing incoming-message.", e);
}
}
// ...
}
As i said If any exception occurred in steps 1-3, i want insert an error-record:
catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
//send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
It's SqlCommandHandlerServiceImpl.persist() method:
public class SqlCommandHandlerServiceImpl implements SqlCommandHandlerService {
// ...
#Override
#Transactional
public void persist(IncomingXmlModel incomingXmlModel) {
Collections.sort(incomingXmlModel.getTables());
List<ParametricQuery> queries = generateSqlQueries(incomingXmlModel.getTables());
for (ParametricQuery query : queries) {
queryExecuter.executeQuery(query);
}
}
// ...
}
But when sqlCommandHandlerService.persist() throws exception (here a org.hibernate.exception.ConstraintViolationException exception), after inserting an error-record in OutgoingMessage table, when the transaction want to be committed , i get UnexpectedRollbackException. I can't figure out where is my problem:
Exception in thread "null#0" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at ir.tamin.branch.insuranceregistration.services.schedular.ReceiveMessagesJob$$EnhancerByCGLIB$$63524c6b.run(<generated>)
at ir.asta.wise.core.util.timer.JobScheduler$ScheduledJobThread.run(JobScheduler.java:132)
I'm using hibernate-4.1.0-Final, My database is oracle, and Here is my transaction-manager bean:
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
This is the normal behavior and the reason is that your sqlCommandHandlerService.persist method needs a TX when being executed (because it is marked with #Transactional annotation). But when it is called inside processNextRegistrationMessage, because there is a TX available, the container doesn't create a new one and uses existing TX. So if any exception occurs in sqlCommandHandlerService.persist method, it causes TX to be set to rollBackOnly (even if you catch the exception in the caller and ignore it).
To overcome this you can use propagation levels for transactions. Have a look at this to find out which propagation best suits your requirements.
Update; Read this!
Well after a colleague came to me with a couple of questions about a similar situation, I feel this needs a bit of clarification.
Although propagations solve such issues, you should be VERY careful about using them and do not use them unless you ABSOLUTELY understand what they mean and how they work. You may end up persisting some data and rolling back some others where you don't expect them to work that way and things can go horribly wrong.
EDIT Link to current version of the documentation
The answer of Shyam was right. I already faced with this issue before. It's not a problem, it's a SPRING feature. "Transaction rolled back because it has been marked as rollback-only" is acceptable.
Conclusion
USE REQUIRES_NEW if you want to commit what did you do before exception (Local commit)
USE REQUIRED if you want to commit only when all processes are done (Global commit) And you just need to ignore "Transaction rolled back because it has been marked as rollback-only" exception. But you need to try-catch out side the caller processNextRegistrationMessage() to have a meaning log.
Let's me explain more detail:
Question: How many Transaction we have? Answer: Only one
Because you config the PROPAGATION is PROPAGATION_REQUIRED so that the #Transaction persist() is using the same transaction with the caller-processNextRegistrationMessage(). Actually, when we get an exception, the Spring will set rollBackOnly for the TransactionManager so the Spring will rollback just only one Transaction.
Question: But we have a try-catch outside (), why does it happen this exception?
Answer Because of unique Transaction
When persist() method has an exception
Go to the catch outside
Spring will set the rollBackOnly to true -> it determine we must
rollback the caller (processNextRegistrationMessage) also.
The persist() will rollback itself first.
Throw an UnexpectedRollbackException to inform that, we need to rollback the caller also.
The try-catch in run() will catch UnexpectedRollbackException and print the stack trace
Question: Why we change PROPAGATION to REQUIRES_NEW, it works?
Answer: Because now the processNextRegistrationMessage() and persist() are in the different transaction so that they only rollback their transaction.
Thanks

How to catch OptimisticLockException in JavaEE 6?

I wonder what is the best way to catch an OptimisticLockException in JavaEE 6. I have the following EJB:
#Stateless
public class SeminarBooking {
public void bookSeminar(Long seminarId, int numberOfPersons) {
...
//check capacity & do booking
//OptimisticLockException can occur in this method
}
And this is my REST interface:
#Path("/seminars")
#Produces("application/xml")
#Stateless
public class SeminarResource {
#GET
#Path("{id}/book")
public Seminar bookSeminar(#PathParam("id") Long id, #QueryParam("persons") Integer persons) {
try {
seminarBooking.bookSeminar(id, persons);
return seminarBooking.getSeminar(id);
}
catch(Exception e) {
//why is this never called?
logger.error(This will never happen, e);
throw new WebApplicationException(e);
}
}
In the REST interface I catch all Exceptions, furthermore I see the OptimisticLockException if I call the interface from the browser, so why is the catch-Block never executed?
The obvious answer is that the exception in question isn't raised within that try block. Try reading the stack trace to see where it's thrown from. Given that it's related to persistence, it's likely thrown at your transaction boundary rather than from where you think it is.
It's probably never called because the SeminarResource is a transactional EJB. That means that the transaction is committed after its bookSeminar() method has returned, and not before it has returned.
This bean should probably not be transactional. If it weren't, the transaction would start when the SeminarBooking service is called, and commit when it returns. And you would be able to catch the exception that the commit throws.

Categories