Spring batch run exception throwable method after each tasklet done in afterstep() - java

public class TaskletConfiguration {
...
#Bean
public Step step() {
return steps.get("step")
.tasklet(tasklet)
.exceptionHandler(logExceptionHandler()) // Handler for logging exception to specific channel
.build();
}
#Bean
public Job job() {
return jobs.get("job")
.start(step())
.build();
}
}
public class ExampleTasklet implements Tasklet, StepExecutionListener {
...
#Override
public RepeatStatus execute(...) throws Exception {
// Do my tasklet
// Throw if it fails, and be handled by logExceptionHandler()
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
// Want to throw so that logExceptionHandler() can handle it as throwing in execute().
throwable_function();
}
}
This is my example code using tasklet in spring boot.
My problem is: I want to throw exception from afterstep(), but the interface does not allow it.
Despite this limitation, why I obsessed with afterstep() is that I want to make abstract class to make Tasklet template which can verify each execution in afterstep(). I want verification to run after all execute() is done, which will be overridden by subclass. So I have no choices but using afterstep().
Any idea to run verification method after each execute() with throwable or afterstep() can pass Exception to logExceptionHandler()? I hope to define logExceptionHandler() in TaskletConfiguration class. It will be obese if it is defined in Tasklet class, as I will make abstract class, which will be inherited by many subclasses.

The StepExecutionListener#afterStep is not designed to throw checked exceptions. Here is an excerpt from its Javadoc:
Called after execution of step's processing logic (both successful or failed).
Throwing exception in this method has no effect, it will only be logged.
Moreover, even if you throw a (runtime) exception in afterStep, the exception won't be passed to the exception handler, it will only be logged as mentioned in the Javadoc.
I think it is too late to throw exceptions in StepExecutionListener#afterStep, this method can be used to check the status of the step execution and modify the ExitStatus if needed to drive the rest of the job execution flow.

Related

How to test for Exception in #Async method?

How can I assert that a certain exception is thrown inside an #Async method?
Because the following exception is caught by Springs SimpleAsyncUncaughtExceptionHandler.
#Service
public class Service {
#Async
public void run() {
throw new RuntimeException();
}
}
public class Test {
#Test
public void test() {
assertDoesNotThrow(() -> service.run()); //this always passes
}
}
If it is possible for your case, separate testing of asynchronicity and the actual unit of work. E.g. write test that will execute (no 'Async' functionality) Service.run() and assert that no/any/some exceptions are thrown.
In second test (utilizing #Async execution) you could test for the actual unit of work i.e. use your Spring provided bean and test for e.g.:
Awaitility.await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(() -> runAnyCodeThatChecksTheResultOfYourServiceRunMethod());
Another method might be to replace the return type of the Service.run() method to java.util.concurrent.Future, Spring will then re-throw the exception. From AsyncExecutionAspectSupport.handleError javadoc:
"If the return type of the method is a {#link Future} object (again, if applicable), the original exception can be propagated by just throwing it at the higher level. However, for all other cases, the exception will not be transmitted back to the client."
import static org.awaitility.Awaitility.await;
Awaitility.await().atMost(5, TimeUnit.SECONDS)
.untilAsserted(() -> assertThrows(RuntimeException.class, () -> service.run()));

Java/Springboot: Catch and handle a particular exception in one place?

Is it possible to catch all thrown exceptions of a particular type, say IllegalArgumentException, throughout the entire Spring Boot application and handle it one place? I want to introduce some additional logging for particular types of exception in application and I am looking for a way to avoid duplicating logic or method calls throughout the application?
Take a look at the annotation #ExceptionHandler(value=YourException.class) and #ControllerAdvice it allows you to handle custom exceptions. The Controller Advice class can handle the exception globally. We can define any Exception Handler methods in this class file.
#ControllerAdvice
public class ProductExceptionController {
#ExceptionHandler(value = ProductNotfoundException.class)
public ResponseEntity<Object> exception(ProductNotfoundException exception) {
return new ResponseEntity<>("Product not found", HttpStatus.NOT_FOUND);
}
}
Spring AOP can be used to address this cross cutting concern.
#AfterThrowing advice can be used for this purpose.
The name used in the throwing attribute must correspond to the name of
a parameter in the advice method. When a method execution exits by
throwing an exception, the exception is passed to the advice method as
the corresponding argument value. A throwing clause also restricts
matching to only those method executions that throw an exception of
the specified type
Example can be found here
Springboot provides us with the capability to handle exceptions globally using the #ControllerAdvice annotation . So, instead of handling exceptions and logging it in each controller, you could actually throw the exception from every controller and handle it in a single place like :
BusinessException extends RunTimeException {
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(value
= { BusinessException.class,IllegalArgumentException.class})
protected ResponseEntity<Object> handleCustomException(
RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.NOTFOUND, request);
}
}
In your case, you could create a custom exception class and throw your custom exception from where ever your custom logic is needed. So, your could then handle this custom exception globally to provide your logic. This is one way to handle exceptions globally without duplicating logic. You could also do this using spring aop using pointcut.
#Aspect
public class LoggingAspect {
#AfterThrowing (pointcut = "execution(* com.yourservice.yourpackage.*(..))", throwing = "ex")
public void logAfterThrowingAllMethods(Exception ex) throws Throwable
{
System.out.println("****LoggingAspect.logAfterThrowingAllMethods() " + ex);
}
}
Just add spring aop and aspectJ as dependencies for this approach.

How to Chain Asynchronous Calls and Propagate Exceptions with CompletableFuture in Spring

I'm very new to multi-threading and CompletableFuture, and I wanted to know if it is possible to implement the following scenario in Spring container (Spring Boot 2) and #Async.
I have several services that basically construct a larger object, and I wanted to see if I can make the granular services run asynchronously.
I have a service (ServiceC) that depends on the output of another service (ServiceA), while the third one can run independently (ServiceB). someService is needed for all the other services, so I thought I don't need to make it an asynchronous call. Below is the layout:
#Service
public class ServiceA {
#Async
public CompletableFuture<A> doServiceAWork(int id) throws AppException {
return CompletableFuture.completedFuture(getA(id));
}
}
#Service
public class ServiceB {
#Async
public CompletableFuture<B> doServiceBWork(int id) {
return CompletableFuture.completedFuture(getB(id));
}
}
#Service
public class ServiceC {
#Async
public CompletableFuture<C> doServiceCWork(A a) {
return CompletableFuture.completedFuture(getC(a));
}
}
#Service
public class ServiceOrchestration {
// Necessary autorwiring of services
public BigObj constructBigObj(AppObj obj) throw SomeAppException {
int id = someService.getId(obj);
BigObj bigObj = new BigObj();
try {
// Conceptualizing here
CompletableFuture<A> cfA = serviceA.doServiceAWork(id);
CompletableFuture<B> cfB = serviceB.doServiceBWork(id);
// Obviously C depends on A
CompletableFuture<C> cfC = serviceB.doServiceCWork(cfA.get());
CompletableFuture.allOf(cfA, cfB, cfC).join();
bigObj.setA(cfA.get());
bigObj.setB(cfB.get());
bigObj.setC(cfC.get());
} catch (AppException | ExecutionException | InterruptedException e) {
throw new SomeAppException("..", e);
}
return BigObj;
}
}
My requirements are as following:
Invoke asynchronous call to ServiceC when ServiceA is done as the returned data should be fed to ServiceC in order to proceed
If a call to ServiceA throws an exception (AppException), it needs to be propagated to the caller -- in this case ServiceOrchestration#constructBigObject(), which handles it as above. In other word, if serviceA.doServiceAWork(id) throws an exception, every other asynchronous call should be canceled but the exception should be thrown in constructBigObj in order to be caught and handled
Wait for all the asynchronous calls to be completed then construct BigObj
For the second requirement, I looked at isCompletedExceptionally() but wasn't sure where it needs to be called from, e.g. before a call to join()? How do I get the root exception then?
How does the asynchronous chaining works here between ServiceA call and ServiceC where there are dependencies for calling one, at the same time, I need values from both, e.g. A and C, to construct BigObj?

EJB Timer : put in a method with Exception, the #Timeout annotation

Good morning everybody,
I write you because i want to create an EJB timer.
But my #Timeout annotated method must throw an application exception.
And i see in documentation that a #Timeout method can't throw an application exception.
#Stateless#Local
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class AccesCacheImpl implements AccesCache {
#Resource
private TimerService serviceMinuteur;
// i have to make an updateDataBase every 30 minutes
public void creerTimer() {
serviceMinuteur.createTimer(1000, 1800000, "MinuteurCache");
}
public void detruireTimer() {
Timer timerCourant;
for (final Object timer : serviceMinuteur.getTimers()) {
timerCourant = (Timer) timer;
if ("MinuteurCache".equals(timer)) {
timerCourant.cancel();
}
}
}
#Timeout
public void updateDataBase(final Timer pTimer) throws AccesNomenclatureException {
.....
.....
.....
}
}
I begin in EJB programming : do you find my code OK ?
And i have necessary to keep this exception.
Have you faced such a problem ?
Thank you very much.
Application exceptions are intended to provide convey additional information to the caller. In this case, the caller of an #Timeout method is the EJB container, which cannot know the intent of your application exception, so they are not allowed. If you need to signal failure to the container so that it will retry, I recommend catching the AccesNomenclatureException and rethrowing it as EJBException, which is a system exception rather than an application exception. Otherwise, if you just want to ignore the failure, then catch it and log it rather than rethrowing it.

Spring MVC: bind an exception handler to particular method

Good day!
I have a #Controller. Some of its methods throw the same exception, but I want to handle this exceptions in different way.
Is there a way how to bind an #ExceptionHandler to a particular method?
You need to use AOP tools like CDI Interceptor or AspectJ to achieve this cross-cutting concerns. A Concern is a term that refers to a part of the system divided on the basis of the functionality.
Basically this type of feature is used to handle logging, security and also handling the errors... which are not part of your business logic...
Like if you want to change the logger for application from log4j to sl4j then you need to go through each and every classes where you have used log4j and change it. But if you have used AOP tools then you only need to go the interceptor class and change the implementation. Something like plug and play and very powerful tool.
Here is a code snippet using JavaEE CDI Interceptor
/*
Creating the interceptor binding
*/
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface BindException {
}
After we have define interceptor binding we need to define interceptor binding implementation
/*
Creating the interceptor implementation
*/
#Interceptor
#BindException
public class ExceptionCDIInterceptor {
#AroundInvoke
public Object methodInterceptor(InvocationContext ctx) throws Exception {
System.out.println("Invoked method " + ctx.getMethod().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// code for Exception handler
}
}
}
Now we only need to apply interceptor to our method
/*
Some Service class where you want to implement the interceptor
*/
#ApplicationScoped
public class Service {
// adding annotation to thisMethodIsBound method to intercept
#BindException
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionCDIInterceptor
}
}
You can achieve same feature using AspectJ also.
/*
Creating the Aspect implementation
*/
#Aspect
public class ExceptionAspectInterceptor {
#Around("execution(* com.package.name.SomeService.thisMethodIsBound.*(..))")
public Object methodInterceptor(ProceedingJoinPoint ctx) throws Throwable {
System.out.println("Invoked method " + ctx.getSignature().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// codes for Exception handler
}
}
}
Now we only need to enable the AspectJ to our application config
/*
Enable the AspectJ in your application
*/
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public SomeService SomeService() {
return new SomeService();
}
}
/*
Some Service class where you want to implement the Aspect
*/
package com.package.name;
public class SomeService {
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionAspectInterceptor
}
}
I have code example in my git repo https://github.com/prameshbhattarai/javaee-exceptionBinding by using CDI interceptor.
Just as an option (obviously, it's not ideal): you can wrap the exception into a custom exception in one of your methods and then catch it in an #ExceptionHandler
void boo() throws WrappingException {
try {
} catch (TargetException e) {
throw new WrappingException(e);
}
}
then
#ExceptionHandler(WrappingException.class)
public void handleWrappingException() {
// handle
}
#ExceptionHandler(TargetException.class)
public void handleTargetException() {
// handle
}
Could you please explain why do you need this? I'm asking out of curiosity, because I've never felt like this is required and here is why:
Exception usually represents a very specific "mistake" - something that went wrong in a very specific way.
Basically, exception represents a mistake, not a flow...
There are two "degrees of freedom" that spring can support out of the box:
Exception parameters. Maybe stuff like error code, which can be declared as a data field of exception itself.
Exception inheritance. Example:
If you have in your system a UserDoesNotExistException and you want to be more specific in a case of say, the system that manages the users that are retired in some flows, you can always create a more specific exception:
class UserRetiredException extends UserDoesNotExistException {...}
Obviously, spring can support both the cases: In ExceptionMapper you have the access to the exception anyway so you can do something like:
handleException(SomeExceptionWithErrorCode ex) {
if(ex.getErrorCode() == "A") {
// do this
}
else if(ex.getErrroCode() == "B") {
// do that
}
}
In the second case you just have different exception mappers for the different types of exceptions.
You can also consider #ControllerAdvice annotation to reuse code or something.
I don't think you can specify a specific #ExceptionHandler for a method, but you can bind an #ExceptionHandler method to a specific Exception.
So if you want to handle all DataIntegrityViolationException one way and all other Exceptions in another you should be able to achieve that with something like this:
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleIntegrityViolation() {
// do stuff for integrity violation here
}
#ExceptionHandler(Exception.class)
public void handleEverythingElse() {
// do stuff for everything else here
}
You can derive sub-exceptions from the common exception thrown by other methods according to how you want to handle them.
Say you have declared the parent exception as ParentException. Derive sub classes like ChildAException extends ParentException, ChildBException extends ParentException etc.
Define a #ControllerAdvice class that catches the ParentException and define the specific behaviors in delegate methods.
#ControllerAdvice
public class ParentExceptionHandler {
#ExceptionHandler(ParentException.class)
public ResponseEntity<Object> handleParentException(ParentException pe) {
if (pe instanceof ChildAException) {
return handleChildAException((ChildAException) pe);
} else if (...) {
...
} else {
// handle parent exception
}
}
private ResponseEntity<Object> handleChildAException(ChildAException cae) {
// handle child A exception
}
}
I just got the same issue like you. So I checked the spring source code for this situation. It seems that spring will search in the #Controller class for any method that is annotated with #ExceptionHandler first, if nothing matched then it will continue to search for all class that is annotated with #ControllerAdvice. So you can just use the strategy below:
MyController with a #ExceptionHandler method:
#RestController
public class MyController {
#RequestMapping("/foo")
public String foo() {
throw new IllegalArgumentException();
}
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Specific handler", HttpStatus.BAD_REQUEST);
}
}
AnotherController without any method annotated with #ExceptionHandler:
#RestController
public class AnotherController {
#RequestMapping("/bar")
public String bar() {
throw new IllegalArgumentException();
}
}
A global #ControllerAdvice class:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Global handler", HttpStatus.BAD_REQUEST);
}
}
Then if you visiting http://ip:port/foo, you will get 400 status code with Specific handler, and 400 status code with Global handler when you visit http://ip:port/bar.
I agree that the inability to map a specific #ExceptionHandler to handle only one specific method in the #RestController should be a very desirable feature.
I tried try{}catch(Exception ex){} and catch no exception. But
exception handler handles it nicely.
Since we are talking about hibernate exceptions, these exceptions are usually thrown at the commit phase of transaction. The problem here is that seems like you have transaction opened right in your controller which is considered as a bad practice.
What you should do is - open transaction in the application layer.
Controller just maps xml/json to incomming RequestDto object.
And then you call the Service to handle the business logic.
The Service(or its method) should be annotated by #Transactional.
#RestController
public class MyController {
#Autowired // but better to use constructor injection
private MyService myService;
public ResponseDto doSomething(RequestDto request) {
try {
myService.doSomething(request);
} catch (DataIntegrityViolationException ex) {
// process exception
}
}
}
#Transactional
class MyService {
public void doSomething() {
// do your processing which uses jpa/hibernate under the hood
}
}
Once you done that, the try catch will start behaving as expected on controller level.
However, I would even go further as DatabaseExeption shouldn't really go that far to controller. The alternative would be to use manual transaction inside of a service and do a try catch there.
Then in the Service layer transform database exception in a more generic exception with all necessary information for controllers to process.
And then you should catch that more generic exception (MyDatabaseAccessException) in the controller and transform as you wish for the sake of a presentation layer.
===
The #ControllerAdvice suggested here is good for a global exception handling across controllers.
The #ExceptionHandler is not suitable for each method unless you wnat to have controller per method. And even after that it can clash with global #ControllerAdvice.
I am not sure why spring doesn't allow #ExceptionHandler at a method level, it would simplify a lot of cases like yours.
My solution is to annotate a method with a marker:
#ExceptionHandler(SomeException.class)
public ResponseEntity<String> handleSomeException(SomeException e, HandlerMethod handlerMethod) {
var marker = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getMethod(), MarkerAnnotation.class);
if (marker != null) return something();
else return somethingElse();
}

Categories

Resources