Spring #Transaction annotation and Exception Handling - java

Consider the following code snippet. (I am using Spring 3.1 and Hibernate 3.6)
#Override
#Transactional
public <T extends Termination> void progressToPendingStage(Class<T> entity,
Long terminationId, String userName) throws Exception {
Termination termination = findTerminationById(entity, terminationId);
//TODO improvise such that email does not get sent if data is not saved
if (termination.getStatus().equals(TerminationStatus.BEING_PREPARED.toString())) {
termination.setStatus(TerminationStatus.PENDING.toString());
termination.setSubmittedDate(new Date());
termination.setSubmittedBy(userName);
saveOrUpdateTermination(termination);
//Send an email to SAS
emailHelper.configureEmailAndSend(termination);
}
}
Unit tests for the above method indicate that email will be sent regardless that the saveOrUpdateTermination(termination) throws an exception or not. On further testing and some research I have uncovered that this behavior is the expected behavior. This is not what the business rules desire. An email should be sent only if the termination record was saved successfully. Any suggestions on how to make this behave in the desired manner? One way I can think of is to make the caller handle the exception thrown by the progressToPendingStage method and if no exception was thrown send an email. Am I on the right track or can we alter the way #Transaction behaves.

I have solved this issue by designing around the problem. Sending an Email was never meant to be part of the transaction. I created an object that performed post saving tasks. The object will catch the exception thrown upon saving the termination and if no exceptions were thrown I would then trigger an email to be sent out. One could also put this in an Spring Aspect which could be executed upon successfully returning after a successful save.
Lessons learn't: Don't include steps that don't belong in a method marked with #transaction. If its included in a transaction Spring will silently handle the exception and not throw the exception till the transaction is finished. In short if a method is annotated with #Transaction every line in that method will be execute even though a line in the middle of the method throws an exception.

Related

Axon framework: Handling database exceptions in state-stored aggregates

I am new to Axon framework and am trying to implement an application using CQRS with state-stored aggregates. The application relies on a database constraint (using H2 for now) to enforce uniqueness on a name attribute. I would like to catch this exception and rethrow it as a user-friendly domain exception.
Per the Axon documentation:
Exception Handling says "an #ExceptionHandler will only handle exceptions thrown from message handling functions in the same class"
Message Intercepting documentation says "A function annotated with #ExceptionHandler will be regarded as a handler interceptor which will only be invoked for exceptional results. Using annotated functions to this end for example allow you to throw a more domain specific exception as a result of a thrown database/service exception."
But I cannot get this to work. I have tried adding exception handlers as follows:
#ExceptionHandler
public void handle(ConstraintViolationException ex) throws Exception {
if (ex.getMessage().contains("UNQ_COMPANY_ID") || ex.getMessage().contains("UNQ_PLAN_NAME")) {
throw new DomainException("Plan name and company id must be unique");
}
throw ex;
}
but this method is not called. I have tried putting the exception handler method on the aggregate and on a separate command handler class, tried adding resultType=ConstraintViolationException.class, and tried catching other types of exceptions including Exception, RuntimeException, AxonServerRemoteCommandHandlingException, etc. but this method is never called.
I can see the error in the log output:
org.axonframework.axonserver.connector.command.AxonServerRemoteCommandHandlingException: An exception was thrown by the remote message handling component: org.hibernate.exception.ConstraintViolationException: could not execute statement
Is it possible to catch database exceptions in state-stored aggregates? If it is, can someone point me towards what I am doing wrong?
The statement "an #ExceptionHandler will only handle exceptions thrown from message handling functions in the same class" makes me wonder whether I need to create a custom repository class (rather than using the default GenericJpaRepository) but that seems like a lot more work than should be necessary.
Thank you!
Update: I was able to roughly accomplish what I want by adding a UnitOfWork parameter to the #CommandHandler method and using it to registering a rollback callback on it as follows:
uow.onRollback(unit -> {
DefaultUnitOfWork duow = (DefaultUnitOfWork) unit;
Throwable ex = duow.getExecutionResult().getExceptionResult();
while (ex != null) {
if (ex.getMessage().contains("UNQ_PLAN_NAME")) {
throw new RuntimeException("Plan name must be unique");
}
ex = ex.getCause();
}
});
But this seems kind of verbose, as well as limiting me to throwing unchecked exceptions only. This also doesn't feel like the right way to do this though because I assume the purpose of the #ExceptionHandler annotation is to eliminate need for code like the above.
This is doable of course.
Actually, the best pointer I could give you if the code-samples repo where you can see a sample about distributed exceptions.
In general, as you could see in your shared log, the 'original' exception is wrapped into an AxonServerRemoteCommandHandlingException meaning you will have to handle that. Doing that, you can pretty much add anything to the details field of this class, adding the indication you had a ConstraintViolationException for example (or an ERROR_CODE, like HTTP protocol does) and you are fine to unwrap it on the other side.
What might be the "gotcha" you require, is to know that an #ExceptionHandler annotated method should reside in the object handling the message. So if you want to react to a failing command handling operation (which would be the case in your sample), you will need to place the exception handler in the Aggregate, next to the Command Handler.
That fact you get an AxonServerRemoteCommandHandlingException to me suggests the exception is caught on the command dispatching end. So, prior to dispatching a command on the CommandGateway/CommandBus.
However, whether this is the problem at hand, isn't clear to me right now, since the sample only shows the exception handler and not where it resides. Please share whether my assumption on the placement of the exception handler is correct. If not, we'll dive into this deeper to get to the cause.

Precondition for Camunda process in a REST application

In some cases when modeling a process with camunda one might have a precondition that has be to executed. If the precondition fails, the process instance should be stopped, returning an error event.
Now, the problem is that, once the process instance is started, you cannot return a mapped error code as an exception to the client who send the request, since an error can only be caugth with a listener.
public void createEntity(SomeEntity e) {
repository.save(e);
// start a workflow with camunda
startWorkflow(e);
/*
* If the precondition fails, it would be nice to throw an
* exception here, indicating 400 (Bad Request)
*/
}
If this is not possible, a simple solution would be to exclude that part from the workflow and only start it if the precondition is successful. But then, you cannot model the complete process with camunda, only a part of it. Returning 204 (No Content) and fetching the result of the process instance in a second query also seems wrong to me.
So, can you wait for a process instance to finish or at least return a mapped error code to the client within the request?

How to handle EJBException in cases where execution should continue

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?

Silently roll transaction back in Payara 4.1

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.

Building reliability in Spring JPA webapp

I am trying to find a solution to build reliability into our webapp. The plan is to dump sql along with data if network connectivity/database connection is lost. In current implementation we have Rest controller, Service, DAO. The DAO throws PersistenceExcetpion, and that is propagated till the Controller layer.
Example code:
public MyDAOClass {
public void save(Object object) {
try {
entityManager.persist(object);
} catch (PersistenceException e) {
throw new DBException("Error occurred in save", e);
}
}
}
The DBException is a runtime exception.
Now the comes the actual question. One of the teammate suggested to have custom exceptions like for eg. InsertException, UpdateException etc. And if we encounter any of these exceptions we know which operation was performed on that entity so that it can be saved to a file as appropriate sql.
For example. Lets say the code failed to save Employee entity. This will throw InsertException, and will create an entry in file as insert sql statement for that entity. insert into employeee values ('firstname','lastname');
For me the idea of implementing the creation of sql file when connectivity is lost doest not seem to be as simple as implementing the above.
The questions that I have put forward are
1) How do you handle when multiple actions (like any combination of insert, update, delete) are performed in the service method ?
2) What about different exceptions ? I mean the reason for PerisistenceException can be anything like constraint failure, entity not found etc and not just the connection issue.
Is there any way to implement the above scenario which also considers all the different conditions.
Thanks.
Update:
Based on comments by chrylis. I should have already added this to the question. It's a webapp running locally in different retail stores. And the application can't have a downtime, so if any connectivity issues, the app should keep work. The file will be later synched with the central database server.
With spring you have Hibernate ORM that will store the data to the database. If an exception occurs during any request it will be rolled back by hibernate. This depends on where you'we put the #Transnational annotation.
We use a Service layer that handles the transaction. So if a database operation or any other operation fails in the service layer and throws an exception the transaction is auto rolled back by hibernate. We then use a spring exception resolver to handle any exception and write custom errors in the log and to the user. I guess you could store the exception in another database as well if that is interesting I think logging them should suffice though.
This article teaches you more about general exception handling.
Here is our exception resolver.
import ...
#ControllerAdvice
public class SpringExceptionResolver {
Logger logger = LoggerFactory.getLogger("com.realitylabs.event.controller.RecoverController");
#ExceptionHandler({CorruptedSessionUserException.class})
#ResponseBody
#ResponseStatus(value=HttpStatus.FORBIDDEN)
public ErrorObject userNotFoundExceptionHandler() {
// Handle exception
// log using the logger.
// We usually return an error object in JSON so that we can show custom // error messages.
}
}
Here is how a service might look. We usually call our services from the controllers. If an exception is thrown when coming from a controller the advice will handle it.
import ...
#Service(value="ObjectService")
#Transactional
public class ObjectServiceImpl implements ObjectService {
#Autowired
private ObjectDAO objectDAO;
#Override
public Object get(int id) {
Object o = objectDAO.get(id);
Hibernate.initialize(o.getVoters());
return o;
}
}
I hope this helps.

Categories

Resources