I have an application that runs many asynchronous process that can take ten or twenty minutes.
On error, I use a AsyncUncaughtExceptionHandler that only writes a log error. I need to notify the user that something wrong happened.
Now I am using a "DelegatingSecurityContextAsyncTaskExecutor" which shares the spring security user between threads. But when I logout, it is deleted and the user in the context null.
This not work fine for me, I want the user who ran the method, not the current in the context,I need him to send a message to your personal mailbox.
How I can get the user that execute the method?
My code:
#Configuration
#EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
#Override
#Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(1);
executor.setThreadGroupName("MyCustomExecutor");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setBeanName("asyncExecutor");
executor.initialize();
DelegatingSecurityContextAsyncTaskExecutor securityExecutor = new DelegatingSecurityContextAsyncTaskExecutor(executor);
return securityExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private static final Logger LOG = LogManager.getLogger(CustomAsyncExceptionHandler.class);
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
LOG.error(
String.format("Error in async method %s with params: %s: Excepcion: %s",
method.getName(), params.toString(), ExceptionUtils.getStackTrace(ex)), ex);
}
}
Related
I have a requirement. I have 2 processes
Contact creation and
Associating contact to the Department
Currently I have a spring boot API which has a REST POST call to perform both in one thread. Since process 2 is taking more time I wanted to run that in the
background immediately after finishing the step 1.
#PostMapping(value = "/processDeptContact", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PayloadResponse> processDeptContact(#RequestBody String payload) {
ResponseEntity response = new ResponseEntity(new ErrorResponse("Exception"),
new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
try {
response = myService.processPayload(payload);
} catch (Exception e) {
logger.error("Exception in the controller");
}
return response;
}
I want to return the response to the user as soon as step 1 is done and performing step 2 at the background. How do I achieve that
Thanks in advance
In your main class, or a #Configuration class, use #EnableAsync to bootstrap a thread pool:
#EnableAsync
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
You can optionally set Thread Pool properties under spring.task.execution.pool property. Example:
spring:
task:
execution:
pool:
core-size: 8
max-size 16
Here's a stack post detailing what each property means: Core pool size vs maximum pool size in ThreadPoolExecutor
Inside your controller:
#RestController
public class TestController {
private final ContactService contactService;
private final DepartmentService departmentService;
// Constructor Injection
public TestController(ContactService contactService, DepartmentService departmentService) {
this.contactService = contactService;
this.departmentService = departmentService;
}
#PostMapping(value = "/processDeptContact", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PayloadResponse> processDeptContact(#RequestBody String payload) {
List<Contact> contacts = contactService.processPayload(payload);
departmentService.associateContacts(contacts); // This is an asynchronous call
return ResponseEntity.ok(new PayloadResponse(contacts));
}
}
I've removed the try/catch from the controller method since error handling is a cross cutting concern and is handled by AOP. More on that here: Baeldung
And finally in your DepartmentService, you use the #Async annotation to turn it into an asynchronous method:
#Service
public class DepartmentService {
#Async
public void associateContacts(List<Contact> contacts) {
// method
}
}
I see other answers are basically saying the same thing and are correct, but not complete so I felt the need to put everything together for you.
Spring framework provides support for asynchronous processing out of the box. Spring can create & manage threads for us by providing support for various TaskExecutor abstraction.
We can create a method in a new class that will do the second process (associate contact to the Department) and annotate that method with #Aysnc. The annotation ensures the spring executes this method as a Runnable/Future depending on return type.
Sample Implementation (We have to add #EnableAsync in any of our configuration class)
#Component
class ContactManager {
#Async
public void associateContactToDepartment(){
//method implementation goes here
}
}
class MyService {
#Autowired
private ContactManager contactManager;
public PayloadResponse processPayload(String payload){
payloadResponse payloadResponse = createContact();//first process
contactManager.associateContactToDepartment() // this call will be executed asynchronously.
return payloadResponse;
}
}
Refer this for quick intro to async methods.
Follow the below steps:
Add #EnableAsync annotation and Add TaskExecutor Bean to main spring boot application class
#SpringBootApplication
#EnableAsync
public class AsynchronousSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(SpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootApplication.class,args);
}
Add the contact to department method as below:
#Service
public class DepartmentProcess {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public void processDepartment() {
logger.info("Received request to process in DepartmentProcess.processDepartment()");
try {
Thread.sleep(15 * 1000);
logger.info("Processing complete");
}
catch (InterruptedException ie) {
logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
}
}
}
Call the method from the controller as below:
#PostMapping(value = "/processDeptContact", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PayloadResponse> processDeptContact(#RequestBody String payload) {
ResponseEntity response = new ResponseEntity(new ErrorResponse("Exception"),
new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
try {
response = myService.processPayload(payload);
myService.processDepartment();//async method
} catch (Exception e) {
logger.error("Exception in the controller");
}
return response;
}
Points 1 and 2 are not here but it doesn't matter, let's call them foo1() and foo2().
In myService.processPayload() you want to do:
ResponseEntity result = foo1();
Runnable runnable = () -> {
foo2()
};
Thread thread = new Thread(runnable);
thread.start(); // the logic in foo2 will happen in a background thread so it will not block on this line, consider using a thread pool instead
return result;
BTW, this sounds like premature optimization and you should think about race conditions with parallel threads but this is not what the question was asking.
One more thing, move this to the catch because it's a waste of instantiations if the try will succeed, which should happen most of the time.
ResponseEntity response = new ResponseEntity(new ErrorResponse("Exception"),
new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
I've a Spring Boot 2.2 application. I created a service like this:
#Async
#PreAuthorize("hasAnyRole('ROLE_PBX')")
#PlanAuthorization(allowedPlans = {PlanType.BUSINESS, PlanType.ENTERPRISE})
public Future<AuditCdr> saveCDR(Cdr3CXDto cdrRecord) {
log.debug("Current tenant {}", TenantContext.getCurrentTenantId());
return new AsyncResult<AuditCdr>(auditCdrRepository.save(cdr3CXMapper.cdr3CXDtoToAuditCdr(cdrRecord)));
}
this is my #Async configuration:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("threadAsync");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
}
Using SecurityContextHolder.MODE_INHERITABLETHREADLOCAL I see the Security context is passed to the #Async method.
In my multi-tenant application I use a ThreadLocal to set the tenant's id:
public class TenantContext {
public final static String TENANT_DEFAULT = "empty";
private static final ThreadLocal<String> code = new ThreadLocal<>();
public static void setCurrentTenantId(String code) {
if (code != null)
TenantContext.code.set(code);
}
public static String getCurrentTenantId() {
String tenantId = code.get();
if (StringUtils.isNotBlank(tenantId)) {
return tenantId;
}
return TENANT_DEFAULT;
}
public static void clear() {
code.remove();
}
}
Because ThreadLocal is related to the thread, it's not available in the #Async method. Furthemore my custom #PlanAuthorization aop needs it to perform verifications of the tenant's plan.
Is there a clean way to set TenantContext in any #Async method in my application?
I ended up to use a TaskDecorator:
#Log4j2
public class MdcTaskDecorator implements TaskDecorator {
#Override
public Runnable decorate(Runnable runnable) {
// Right now: Web thread context !
// (Grab the current thread MDC data)
String tenantId = TenantContext.getCurrentTenantId();
Long storeId = StoreContext.getCurrentStoreId();
SecurityContext securityContext = SecurityContextHolder.getContext();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
log.info("Saving tenant information for async thread...");
return () -> {
try {
// Right now: #Async thread context !
// (Restore the Web thread context's MDC data)
TenantContext.setCurrentTenantId(tenantId);
StoreContext.setCurrentStoreId(storeId);
SecurityContextHolder.setContext(securityContext);
MDC.setContextMap(contextMap);
log.info("Restoring tenant information for async thread...");
runnable.run();
} catch (Throwable e) {
log.error("Error in async task", e);
} finally {
MDC.clear();
}
};
}
}
and I used it in this way:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("threadAsync");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
}
It works and it seems also a neat solution.
The solution for such case is to :
configure custom thread pool so that you override it's execute method to sets up your thread local (or
executes any task from your main context), decorate the task and submit decorated task for execution instead of original one
instruct #Async annotation to use concrete thread pool
#Bean("tenantExecutor")
public Executor threadLocalAwareThreadPool() {
final CustomizableThreadFactory threadNameAwareFactory =
new CustomizableThreadFactory("threadAsync");
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10,
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(500), threadNameAwareFactory) {
// override original method of thread pool
#Override
public void execute(Runnable originalTask) {
final String tenantId = tenantThreadLocal.get(); // read data from current before passing the task to async thread
// decorate the actual task by creating new task (Runnable) where you first set up the thread local and then execute your actual task
super.execute(() -> {
tenantThreadLocal.set(tenantId); // set data in actual async thread
originalTask.run();
});
}
};
return threadPoolExecutor;
}
Now we tell spring use our custom executor
#Async("tenantExecutor")
public Future<AuditCdr> saveCDR(Cdr3CXDto cdrRecord) {
// your code....
}
Instead of ThreadLocal you must use InheritableThreadLocal. Then you will see the values from the parent thread.
API Doc: https://docs.oracle.com/javase/8/docs/api/java/lang/InheritableThreadLocal.html
Here is an article about this in combination with Spring: https://medium.com/#hariohmprasath/async-process-using-spring-and-injecting-user-context-6f1af16e9759
Requirements
I need to be able to trigger a (long running) job via a POST call and return immediately.
Only one thread can run the job at one time.
The job being an expensive one, I want all future triggers of this job to not do anything if one job is already in progress.
Code
#RestController
public class SomeTask {
private SomeService someService;
#Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
#Async // requirement 1
#RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
expensiveLongRunningJob();
}
/**
* Synchronized in order to restrict multiple invocations. // requirement 2
*
*/
private synchronized void expensiveLongRunningJob() {
someService.executedJob();
}
}
Question
With the above code requirements 1 and 2 are satisfied. What is the best way to satisfy requirement 3 as well (have the new thread, created as a result of a POST call, skip the synchronised method and return immediately on failure to acquire a lock)?
Synchronization isn't the right tool for the job. You can do it like this:
#RestController
public class SomeTask {
private SomeService someService;
private final AtomicBoolean isTriggered = new AtomicBoolean();
#Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
#Async // requirement 1
#RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
if (!isTriggered.getAndSet(true)) {
try {
expensiveLongRunningJob();
} finally {
isTriggered.set(false);
}
}
}
/**
* only runs once at a time, in the thread that sets isTriggered to true
*/
private void expensiveLongRunningJob() {
someService.executedJob();
}
}
For requirement 1, if you want to use just #Async, you should have it on the service method and not the controller method. But be aware that by making it async, you would lose control over the job and failure handling will be not possible, unless you implement #Async with Future and handle failures by implementing AsyncUncaughtExceptionHandler interface.
For requirement 3, you can have a volatile boolean field in the service, which gets set just before beginning the job process and unset after the job process completes. In your controller method, you can check the service's volatile boolean field to decide if the job is being executed or not and just return with appropriate message if the job is in progress. Also, make sure to unset the boolean field while handling the failure in the implementation of AsyncUncaughtExceptionHandler interface.
Service:
#Service
public class SomeService {
public volatile boolean isJobInProgress = false;
#Async
public Future<String> executeJob() {
isJobInProgress = true;
//Job processing logic
isJobInProgress = false;
}
}
Controller:
#RestController
public class SomeTask {
#Autowired
private SomeService someService;
#RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
if (!someService.isJobInProgress){
someService.executeJob(); //can have this in a sync block to be on the safer side.
} else {
return;
}
}
}
Implementation of AsyncUncaughtExceptionHandler:
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private SomeService someService;
#Override
public void handleUncaughtException(
Throwable throwable, Method method, Object... obj) {
//Handle failure
if (someService.isJobInProgress){
someService.isJobInProgress = false;
}
}
}
#Async configuration:
#Configuration
#EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
Does Spring Boot have something that allows to run a task several minutes later after a call?
Update:
Found that Spring Boot has ThreadPoolTaskScheduler. That allows to implement one time task execution with a time delay. See the answer below
First, we need to define a Bean
#Bean
public ThreadPoolTaskScheduler getScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
return scheduler;
}
The task that needs to be executed
public class WakeUpCallTask implements Runnable {
private String parameter1;
private String parameter2;
public WakeUpCallTask(String parameter1, String parameter2) {
super();
this.parameter1= parameter1;
this.parameter2= parameter2;
}
#Override
public void run() {
// Code that should be executed
}
}
Endpoint example - scheduling a task in 10000ms
#Autowired
ThreadPoolTaskScheduler scheduler;
#PostMapping(value = "/wake-me-up", consumes = "application/json")
#ResponseStatus(HttpStatus.OK)
public void scheduleCall(#RequestBody WakeMeUpRequest request) {
scheduler.schedule(
new WakeUpCallTask(request.getParameter1(), request.getParameter2()),
new Date(System.currentTimeMillis() + 10000));
}
I am trying to create a Async REST Service using RestEasy, but I can't find any documentation that shows a clean way of doing so. The only example I found is here:
https://github.com/resteasy/Resteasy/blob/master/jaxrs/async-http-servlet-3.0/async-http-servlet-3.0-test/src/main/java/org/jboss/resteasy/test/async/JaxrsResource.java
#GET
#Produces("text/plain")
public void get(#Suspended final AsyncResponse response) throws Exception
{
response.setTimeout(2000, TimeUnit.MILLISECONDS);
Thread t = new Thread()
{
#Override
public void run()
{
try
{
System.out.println("STARTED!!!!");
Thread.sleep(100);
Response jaxrs = Response.ok("hello").type(MediaType.TEXT_PLAIN).build();
response.resume(jaxrs);
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
t.start();
}
Creating a new thread in the method doesn't seem like the best way to do things in a production environment. I feel like I should be getting a thread from a thread pool or something.
Any suggestions or links to better examples would be very helpful.
You are right about using Thread pool. Let Spring take care of it for you.
Assuming you are using Spring as a choice for your application you could do something like.
You can define an Configuration class for Async Executors.
#EnableAsync
#Configuration
public class AsyncConfiguration implements AsyncConfigurer {
#Inject
private Environment env;
#Bean
#Override
#Singleton
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(env.getProperty("controller.threadPoolTaskExecutor.corePoolSize", Integer.class, 10));
taskExecutor.setMaxPoolSize(env.getProperty("controller.threadPoolTaskExecutor.maxPoolSize", Integer.class, 100));
taskExecutor.setKeepAliveSeconds(env.getProperty("controller.threadPoolTaskExecutor.keepAliveSeconds", Integer.class, 60*5));
taskExecutor.setQueueCapacity(env.getProperty("controller.threadPoolTaskExecutor.queueCapacity", Integer.class, 10000));
taskExecutor.setThreadNamePrefix("controller-async-task-executor");
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
Here is how you can define the Exception Handler:
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(AsyncExceptionHandler.class);
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
logger.error("Error processing async request in method: " + method.getName(), ex);
}
}
Here is how your controller could look like:
#Inject
private AsyncTaskExecutor asyncTaskExecutor;
#GET
#Produces("text/plain")
public void get(#Suspended final AsyncResponse response) throws Exception
{
response.setTimeout(2000, TimeUnit.MILLISECONDS);
asyncTaskExecutor.submit(() -> {
System.out.println("STARTED!!!!");
Thread.sleep(100);
Response jaxrs = Response.ok("hello").type(MediaType.TEXT_PLAIN).build();
response.resume(jaxrs);
});
}