package com.sinata.appointment.config; import java.util.Map; import java.util.concurrent.ThreadPoolExecutor; import org.slf4j.MDC; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration public class AsyncConfig { @Bean(name = {"taskExecutor", "applicationTaskExecutor"}) public ThreadPoolTaskExecutor applicationTaskExecutor() { return buildExecutor(4, 8, 200, "appointment-async-"); } private ThreadPoolTaskExecutor buildExecutor( int corePoolSize, int maxPoolSize, int queueCapacity, String threadNamePrefix) { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(60); executor.setAllowCoreThreadTimeOut(true); executor.setThreadNamePrefix(threadNamePrefix); executor.setTaskDecorator(new MdcTaskDecorator()); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } private static final class MdcTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { Map contextMap = MDC.getCopyOfContextMap(); return () -> { Map previous = MDC.getCopyOfContextMap(); try { if (contextMap == null || contextMap.isEmpty()) { MDC.clear(); } else { MDC.setContextMap(contextMap); } runnable.run(); } finally { if (previous == null || previous.isEmpty()) { MDC.clear(); } else { MDC.setContextMap(previous); } } }; } } }