In spring-batch, how can I get the exception when there is a chunk error? - spring-batch

Spring batch provides a listener for capturing when a chunk error has occurred (either the #AfterChunkError annotation or the ChunkListener.afterChunkError interface). Both receive the ChunkContext and the API says:
Parameters:
context - the chunk context containing the exception that caused the
underlying rollback.
However, I don't see anything on the ChunkContext interface that would get me to the exception. How do I get from the ChunkContext to the relevant exception?

Exception is located in ChunkListener.ROLLBACK_EXCEPTION_KEY attribute:
context.getAttribute(ChunkListener.ROLLBACK_EXCEPTION_KEY)

Instead of implementing a ChunkListener you could implement one or more of ItemReadListener, ItemProcessListener and ItemWriteListener (or more simply ItemListenerSupport).
They respectively give acces to :
void onReadError(java.lang.Exception ex)
void onProcessError(T item, java.lang.Exception e)
void onWriteError(java.lang.Exception exception, java.util.List<? extends S> items)
I know this forces you to implement multiple methods to manage every aspect of a chunk, but you can write a custom method which takes an Exception and call it from these 3 methods.

Related

Spring Batch skip exception and rollback in Tasklet

I want to achieve a tasklet that can skip exceptions and rollback the transaction properly and I don't see a way to accomplish both things.
My tasklet reads from a queue of ids that gets filled in the constructor. In each invocation of the execute method one id is processed and depending on if the queue still has elements to be processed or not a RepeatStatus.FINISHED or RepeatStatus.CONTINUABLE is returned. I am using a tasklet instead of a chunk because the processing of each element is fairly complicated and implies doing multiple queries, instantiation of a lot of objects that all gets written to the database later.
The main problem is if I define a try/catch block to wrap the implementation, I can skip exceptions without problems and still be able to re-execute the tasklet with the next element in the queue, but the problem is that everything gets saved in the database. On the other hand, even if the processing of an element is done correctly without problems, if the commit fails for whatever reason, as the error occurs outside the reach and control of my code, the exception is not caught by my code and the tasklet execution is finished without the possibility to skip and continue with the next element of the queue.
This is a simplified schema of my tasklet:
public MyTasklet() {
elementsIds = myRepo.findProcessableElements();
}
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
Long id = elementsIds.remove();
try {
// Business logic
} catch (Exception e) {
// is there a way to tell the framework to rollback ?
LOG.error("error ...", e);
}
if (elementsIds.isEmpty()) {
return RepeatStatus.FINISHED;
} else {
return RepeatStatus.CONTINUABLE;
}
}
Is there a way to achieve these two requirements with tasklets:
To be able to tell the framework to rollback the transaction if an exception is caught in the implementation of the execute method
To continue the execution (consecutive calls) of the tasklet if a commit fails

remove object out from pool in apache common pool2

I have created GenericPool by extending GenericObjectPool and poolFactory using BasePooledObjectFactory. Now I want to remove the object from my Generic Pool.
.clear() will remove idleObject from pool how do we remove permanently from the pool?
Your poolFactory must implement the method
void destroyObject(PooledObject<T> p) throws Exception;
While your code should be calling method
public void invalidateObject(final T obj) throws Exception
on the GenericPool
At runtime, if you run into an exception situation (perhaps caused by a temp network issue) you need to remove the object from the pool and re-create it, so calling invalidateObject will destroy this object and automatically create new one when you call borrowObject

Spring Batch: Listener event when Tasklet throws an exception

I'm using a tasklet and a StepExecutionListener but it seems there's no listener callback for a case where my tasklet throws an exception. For various other listener types – ChunkListener, ItemProcessListener, etc. – there is but none of those listeners work with tasklets.
All I want is an event after my tasklet executes regardless of whether it threw an exception or not. Is it possible to do that? It doesn't appear to be supported in the API.
Edit: Responding to #danidemi I'm registering the listener and tasklet using the programmatic API like this:
steps.get(name)
.listener(listener)
.tasklet(tasklet)
.build()
Where steps is an instance of StepBuilderFactory.
You can
manage exception into tasklet
store error into execution-context/external bean
manage error from stepExecutionListener
or in StepExecutionListener.afterStep(StepExecution stepExecution) lookup into stepExecution.getFailureExceptions()
I guess its too late now. But I recently came across this problem. It is easier to handle exception with ChunkListener, however exception handling can be done in Tasklet's RepeatStatus execute(StepContribution s, ChunkContext chunkContext) method (well, it is the only method that Tasklet interface has ^^). What you need is a try/catch block to catch the exceptions. However you will need to throw the caught exception again in order to roll back the transaction.
Here is a code-snippet. In my case, I had to stop the job if some data could not be read due to Database server shutdown.
#Override
public RepeatStatus execute(StepContribution s, ChunkContext chunkContext){
try{
getAllUsersFromDb(); // some operation that could throw an exception
// doesn't hurt to put all suspicious codes in this block tbh
}catch(Exception e){
if(e instanceof NonSkippableReadException){
chunkContext.getStepContext().getStepExecution().getJobExecution().stop();
}
throw e;
}
return RepeatStatus.FINISHED;
}

JAX-RS exception handling

I'm relatively new to REST services in Java. I've created one and everything works fine except error handling. If I make a request with incorrectly formed JSON in it, Jackson JSON processor throws an exception which I unable to catch and I get error 500 in client. Exception follows:
javax.ws.rs.InternalServerErrorException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.HashSet out of VALUE_STRING token
I have no idea how to handle exceptions raised outside my code.
Google suggests using Exception Mappers or Phase Inteceptors. Though I could miss something in search results.
What is the proper way to handle such situations?
Please, advise something. I'm stuck with this problem...
A JAX-RS ExceptionMapper should do the job. Just add a class like below to your code and if you have the exception type right, then you should get the hook to customize the handling.
#Provider
public class MyExceptionMapper implements ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException ex) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
}

Best way to handle constraint violations when using EJB 3.0 and CMP

I have a web service (built using jaxb/jaxws) that invokes a stateless EJB to store some data in the database. The data is being stored as an entity bean. The entity bean has a unique constraint applied to it via the #Column(unique = true) annotation.
When the web service attempts to save data, the transaction fails, and rightfully so. The problem is that since I am using CMP, the transaction is not committed until after the call to the stateless EJB. The end result is that I am not able to trap the exception and it is getting funneled up to the WS stack and results in an ambiguous fault containing the string: Error committing transaction:;nested exception is: weblogic.transaction.internal.AppSetRollbackOnlyException.
Is there a way to catch the exception being thrown so I can provide more information to the caller? Thank you.
Version information:
Application Server: Oracle Weblogic 10.3
Persistence Provider: Hibernate 3.2.5.ga (JPA 1.0)
JDK/JRE: 1.6_0_05 (provided by Weblogic install)
Update:
I tried to implement an EJB 3 interceptor around the method invocation and this does not appear to work.
public class TestInterceptor {
#AroundInvoke
public Object logCall(InvocationContext context) throws Exception {
System.out.println("Invoking method: " + context.getMethod().getName());
try {
return context.proceed();
} catch (Throwable t) {
System.out.println("I caught an exception: " + t.getMessage());
throw new Exception(t);
}
}
The reason I think this doesn't work is because the processing chain is such that the actual persist happens outside of the method (of course).
You could try using Bean Validation. It's nicely connected with the JPA (invoked during pre-persist, pre-update and pre-remove phases and can be used in different layers of your application.
Unfortunately, as far as I know, if a validation constraint violation occurs, the transaction is marked for rollback... I don't know how you could cope with that but one (seems nasty and untested) way I could think of is to inject a ValidatorFactory and validate the object by yourself. Perhaps then you could catch the ValidationException.
EDIT: I'm not sure if the Bean Validation was available in Java EE 5.
EDIT 2: You can create an interceptor which will catch the exception thrown by the JPA (or more precisely by the database). As the interceptor is invoked as a part of the same transaction as the EJB method you might need to explicitly invoke EntityManager#flush(-) to synchronise changes with the database.