Spring Async Uncaught Exception handler - java

#Override
#Async
public void asyncExceptionTest() {
int i=1/0;
}
How can I log this using Spring Async framework without having to put try catch around every async method? It doesn't seem to pass to the DefaultUncaughtExceptionHandler like normal.

#Async methods can be configured with a custom Executor to log any thrown exceptions.
The following code implements this pattern. Any method tagged with #Async will use the Executor returned by the method public Executor getAsyncExecutor(). This returns the HandlingExecutor which takes care of all logging (in this case it just prints the word "CAUGHT!" but you can replace with logging.
#Configuration
#EnableAsync
public class ExampleConfig implements AsyncConfigurer {
#Bean
public Runnable testExec() {
return new TestExec();
}
#Override
public Executor getAsyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return new HandlingExecutor(executor);
}
}
public class HandlingExecutor implements AsyncTaskExecutor {
private AsyncTaskExecutor executor;
public HandlingExecutor(AsyncTaskExecutor executor) {
this.executor = executor;
}
#Override
public void execute(Runnable task) {
executor.execute(task);
}
#Override
public void execute(Runnable task, long startTimeout) {
executor.execute(createWrappedRunnable(task), startTimeout);
}
#Override
public Future<?> submit(Runnable task) {
return executor.submit(createWrappedRunnable(task));
}
#Override
public <T> Future<T> submit(final Callable<T> task) {
return executor.submit(createCallable(task));
}
private <T> Callable<T> createCallable(final Callable<T> task) {
return new Callable<T>() {
#Override
public T call() throws Exception {
try {
return task.call();
} catch (Exception e) {
handle(e);
throw e;
}
}
};
}
private Runnable createWrappedRunnable(final Runnable task) {
return new Runnable() {
#Override
public void run() {
try {
task.run();
} catch (Exception e) {
handle(e);
}
}
};
}
private void handle(Exception e) {
System.out.println("CAUGHT!");
}
}

Update: Since Spring 4.1
Since Spring 4.1 It is possible to have an AsyncUncaughtExceptionHandler for #Async void methods.
Spring Reference Doc, Chapter 34.4.5 Exception management with #Async
... With a void return type however, the exception is uncaught and cannot be transmitted. For those cases, an AsyncUncaughtExceptionHandler can be provided to handle such exceptions.
By default, the exception is simply logged. A custom AsyncUncaughtExceptionHandler can be defined via AsyncConfigurer or the task:annotation-driven XML element.
(This feature was introduced after DD raised an impovement request: https://jira.spring.io/browse/SPR-8995 , see comments of this answer)
Before Spring 4.1
Looks like an missing feature how to handle exceptions of an void returning #Async Method. (I can not find any hint in the reference or java doc)
What I can imagine of an solution: Try to use AspectJ to write some kind of wrapper arround all #Async methods that log the exceptions.
For the log term, I would recommend to create an freature request in the spring bug tracker.

First off all, you should create a custom exception handler class like following;
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(AsyncExceptionHandler.class);
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
logger.error("Unexpected asynchronous exception at : "
+ method.getDeclaringClass().getName() + "." + method.getName(), ex);
}
}
After that, you should set your customized exception handler class in your configuration like following;
#Configuration
#EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
#Autowired
private AsyncExceptionHandler asyncExceptionHandler;
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
Note : Injectable exception handler is an option. You can create a new instance for every exception. My advice is using Injection for exception handler class, because spring's default scope is singleton so there is no need to create new instance for every exception.

You can use standard Spring AOP approach
#Aspect
#Component
#Slf4j
public class AsyncHandler {
#Around("#annotation(org.springframework.scheduling.annotation.Async)")
private Object handle(ProceedingJoinPoint pjp) throws Throwable {
try {
Object retVal = pjp.proceed();
return retVal;
} catch (Throwable e) {
log.error("in ASYNC, method: " + pjp.getSignature().toLongString() + ", args: " + AppStringUtils.transformToWellFormattedJsonString(pjp.getArgs()) + ", exception: "+ e, e);
throw e;
}
}
}

Related

Spring WebFlux. How to add specific logic after controller call?

I have the next RestController:
#PostMapping("/play")
public Mono<PlayResponse> play(#RequestBody String body) {
//business logic here;
}
And I wanna add some additional logic after the controller call. For example, I want to add to my application some modes logic:
timeout mode - after the successful call, the response will wait until timeout happens
long answer mode - after the successful call, the response will wait a particular amount of ms
failure mode - after the successful call, the response will answer with FORBIDDEN code
etc.
I'm trying to achieve that through WebFilter:
#Component
public class OutgoingFilter implements WebFilter {
Mode mode = new TimeoutMode();
#NonNull
#Override
public Mono<Void> filter(#NonNull final ServerWebExchange exchange, final WebFilterChain chain) {
return chain.filter(exchange)
.doOnNext(this::onNext)
.map(this::onMap)
.doFinally(this::onFinally);
}
private Void onMap(final Void unused) {
mode.run();
return unused;
}
private void onNext(final Void unused) {
mode.run();
}
private void onFinally(final SignalType signalType) {
mode.run();
}
}
As you can see, I've tried onMap, doOnNext andonFinally methods and none of them seem not working.
Is it the right way to use WebFilter there? Maybe I'm doing something wrong?
How can I implement such logic in the Spring WebFlux application?
Update
There is my Mode interface:
public interface Mode {
void run() throws Forbidden;
}
Implementations:
public class Fail implements Mode {
#Override
public void run() throws Forbidden {
throw new Forbidden("Fail mode enabled");
}
}
public class Wait implements Mode {
private final int ms;
public Wait() {
this(0);
}
public Wait(final int ms) {
this.ms = ms;
}
#Override
public void run() throws Forbidden {
sleep();
}
private void sleep() {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
But the implementations aren't final. I will change them to a reactive style if it's needed.

Catch Java exception from Runnable in Unit test

I have the below code
public final class JoinableTaskPool<T> extends ABC {
private int taskCounter;
private final Object monitor;
private final ExtendedThreadPoolExecutor service;
private final CompletionService<T> compService;
public Future<T> submit(final Callable<T> task) {
final Future<T> result = compService.submit(task);
service.submit(new Runnable() {
public void run() {
try {
final Future<T> result = compService.take();
try {
handler.processResult(result);
} catch (final Throwable t) {
throw new SearchException("Task has an error", t);
}
} catch (InterruptedException e) {
throw new SearchException("Task has an error", e);
}
}
}
return result;
}
public void join() {
synchronized (monitor) {
while (taskCounter != 0) {
try {
monitor.wait();
} catch (InterruptedException e) {
error(e, "Interrupted in join");
}
}
}
}
The ExtendedThreadPoolExecutor class is defined as follows
public class ExtendedThreadPoolExecutor extends ThreadPoolExecutor {
public ExtendedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
throw new SearchException("Error while executing the task", t);
}
}
}
I am trying to write a unit test for this method. Below is the method
#RunWith(MockitoJUnitRunner.class)
public class TestJoinableTaskPool<T> {
private JoinableTaskPool<T> pool;
#Before
public void setUp() {
pool = new JoinableTaskPool<T>(1);
}
#After
public void tearDown() {
pool.shutdown();
}
#Test (expected = SearchException.class)
public void testSubmit() throws Exception{
Callable task = (Callable<T>) () -> null;
Mockito.when(pool.getFinishHandler().processResult(result))
.thenThrow(RuntimeException.class);
pool.submit(task);
}
}
Since the SearchException exception is thrown in runnable, there is no way to access it outside the submit method. If I would have returned the Future returned by executorService.submit, I could have done a future.get() to get an exception, but I am returning another future (result variable).
So while writing the unit test I am not able to get the exception thrown.
Also I have overriden the afterExecute() in an effort to catch exception from unit test,but couldnt find a way to call it.
How do I test this exception thrown from the runnable from a unit test.
Any help would be appreciated.
Putting aside that his code smells a mile away, what you can do is to create additional interface eg
public interface MyErrorHandler {
handleError(Exception e)
implement it in your executor pools and call it on exception. Then you can use use Mockito.spy to see if tha method have been called or not on MyErrorHandler (cglib should allow you to do that even withount additional intrerface.)
alternatively you can define that MyExceptionHandler instance should be passed to an executor (via constructor or setter) so you will be able to provide use case dependent implementation of such callback

How to run autowired thread in spring boot

I am struggling to run a thread in background with autowired bean in spring boot. From all the internet source I found that if I create a new instance of the object it will throw null because it is not part of spring life cycle and I would instead need to use executorTask and inject it as bean. Here is what I have tried so far with no luck.
My Application.java file
#SpringBootApplication
#EnableAsync
public class Application {
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
SpringApplication.run(Application.class, args);
}
}
My ThreadConfig.java file [where I actually create the bean for task executor]
#Configuration
public class ThreadConfig {
#Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("default_task_executor_thread");
executor.initialize();
return executor;
}
}
The AsyncService.java file
#Service
public class AsynchronousService {
#Autowired
private ApplicationContext applicationContext;
#Autowired
private TaskExecutor taskExecutor;
public void executeAsynchronously() {
NotificationThread myThread = applicationContext.getBean(NotificationThread.class);
taskExecutor.execute(myThread);
}
}
The actual thread that I want to run in background
#Component
#Scope("prototype")
public class NotificationThread implements Runnable {
#Autowired
private UserDao userDao;
public void run() {
while (true) {
System.out.println("thread is running...");
List<User> users = userDao.findAllByType("1"); //Used to get Error here when running directly from main
try {
Thread.sleep(1000 );
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Before when I directly create this thread in main I would get error as mentioned in the comment line. So i switched to taskexecutor.
NotificationThread is the thread i want to run in background. But its not working, not sure what changes to make. Would help guidance.
Need to call the service method executeAsynchronously() to start the flow.
You may auto-wire AsynchronousService to the ThreadAppRunner as follows and call service.executeAsynchronously().
#Component
public class ThreadAppRunner implements ApplicationRunner {
#Autowired
AsynchronousService service;
#Override
public void run(ApplicationArguments args) throws Exception {
service.executeAsynchronously()
}
}

Spring boot Non Controller Exceptions Handling - Centralized Exception Handling

Is there a way to create a centralized exception handling mechanism in spring boot. I have a custom exception that I am throwing from multiple #Component classes and I would like it to be caught in one class/handler.
This is NOT a REST API or Controller triggered call. I tried #ControllerAdvice with #ExceptionHandler. but no luck. Example below to shows what I am trying to achieve. Method Handle is not triggering. I am using spring boot v2.1.1
CustomException
public class CustomException extends RuntimeException {
public CustomException(String errorMessage, Throwable err) {
super(errorMessage, err);
}
}
Handler
#ControllerAdvice
public class CatchCustomException {
#ExceptionHandler(value = CustomException.class )
public void handle (CustomException e)
{
System.out.println(e.getMessage());
}
}
Component Class
#Component
#EnableScheduling
public class HandlingExample {
#Scheduled(fixedRate = 3000)
public void method1(){
throw new CustomException("Method1++++", new Exception());
}
#Scheduled(fixedRate = 1000)
public void method2(){
throw new CustomException("Method2----", new Exception());
}
}
spring have many error handlers in different context, for your case, you should handle the error exception with #Schedule, so you can create a TaskScheduler by your own
#Bean
public TaskScheduler taskScheduler() {
ScheduledExecutorService localExecutor = Executors.newSingleThreadScheduledExecutor();
ConcurrentTaskScheduler taskScheduler = new ConcurrentTaskScheduler(localExecutor);
taskScheduler.setErrorHandler(new YourErrorHandler());
return taskScheduler;
}
public class YourErrorHandler implements ErrorHandler {
#Override
public void handleError(Throwable t) {
// TODO Auto-generated method stub
}
}

Threadpool task executor timeout for working thread

I am using spring boot and have one async method. To execute async I have below configuration, questions is what if all those 5 thread hangs for some reason , essentially it will lock the application and none of new task will be executed (it will just keep accepting). How we can set timeout for those working thread , lets say 120 seconds, so after that it timesout and execute new task. (Yes I am using fixed thread pool with unbounded queue to keep accepting tasks)
#EnableAsync
#Configuration
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(5);
taskExecutor.initialize();
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
You can create another executor like:
static class TimeOutExecutorService extends CompletableExecutors.DelegatingCompletableExecutorService {
private final Duration timeout;
private final ScheduledExecutorService schedulerExecutor;
TimeOutExecutorService(ExecutorService delegate, Duration timeout) {
super(delegate);
this.timeout = timeout;
schedulerExecutor = Executors.newScheduledThreadPool(1);
}
#Override public <T> CompletableFuture<T> submit(Callable<T> task) {
CompletableFuture<T> cf = new CompletableFuture<>();
Future<?> future = delegate.submit(() -> {
try {
cf.complete(task.call());
} catch (CancellationException e) {
cf.cancel(true);
} catch (Throwable ex) {
cf.completeExceptionally(ex);
}
});
schedulerExecutor.schedule(() -> {
if (!cf.isDone()) {
cf.completeExceptionally(new TimeoutException("Timeout after " + timeout));
future.cancel(true);
}
}, timeout.toMillis(), TimeUnit.MILLISECONDS);
return cf;
}
}
Then, create a new bean named timed
#Bean(name = "timed")
public Executor timeoutExecutor() {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("timed-%d").build();
return TimedCompletables.timed(Executors.newFixedThreadPool(10, threadFactory), Duration.ofSeconds(2));
}
And, try to use this Executor to execute your async tasks.
Or, try to change your code from FixSizeThreadPool to build a own thread pool executor.
You can not submit some task with timeout. What you can do is when you submit your task your get a Future object. You can keep this reference in some Map and pole and see if the task keeps running past your timeout. If so you can use the method cancel() of class Future.
Alternatively, your own task when it starts running places its own current thread into some Map visible to your main (submitting) thread. Also if you see that your task didn't finish in time (again poling) you can try and interrupt your thread. In either case your submitted task should be able to react to interrupt() method of Thread class. I actually implemented this alternative way. If you go this way, test a LOT... :)
I think Future.get(timeout, unit) method can manage async timeout.
Following example can work on my local.
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class AsyncTimeoutExampleAppliation {
private final MyService myService;
public AsyncTimeoutExampleAppliation(MyService myService) {
this.myService = myService;
}
public static void main(String[] args) {
SpringApplication.run(AsyncTimeoutExampleAppliation.class, args);
}
#PostConstruct
void postConstract(){
asyncCall();
}
public void asyncCall(){
try {
String result = myService.doSomething()
.get(10, TimeUnit.SECONDS);
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
#Service
public static class MyService {
#Async
public Future<String> doSomething() throws InterruptedException {
TimeUnit.SECONDS.sleep(60);
return CompletableFuture.completedFuture("Finished");
}
}
}
We can get TimeoutException 10 seconds after application starts.

Categories