Spring Boot start/stop tasks dynamically - java

I have a Spring Boot application, I need to expose 2 endpoints to start and stop scheduled task. The cron/fixed delay expression is defined in database.This configuration is per client.For example if client A calls the start API, I need to start a scheduled task based on the config defined in the database for that client A. The logic inside the task is same for all the clients.
My question is, any no of clients can call start and stop APIs, So how to start the task dynamically when start api is called and also stop the task (only that particular client's task not all ) when the client calls the stop API
`
#RestController
class SchedulerController {
#Autowired
TaskScheduler taskScheduler;
ScheduledFuture<?> scheduledFuture;
#RequestMapping("start")
ResponseEntity<Void> start(#RequestParam String clientId) {
String fixedDelay = "get it from database for the provided client";
scheduledFuture = taskScheduler.schedule(excuteLogic(clientId), fixedDelay);
// generate a task id and save in db against clientid
return new ResponseEntity<Void>(HttpStatus.OK);
}
#RequestMapping("stop")
ResponseEntity<Void> stop(#RequestParam String clientId) {
// get the taskid from database for the clientId
// stop the task
return new ResponseEntity<Void>(HttpStatus.OK);
}
private Runnable excuteLogic(String clientId) {
return () -> {
// logic goes here
};
}
}
Planning to generate a task id and save it in the database against clientid ,but not sure how to stop particular task scheduled for the provided client.

For your use case, since you have a fixed delay, I would save myself the headache and just keep that task running always and depending on a simple flag either return from the worker or continue. So something along the lines of:
#RequestMapping("start")
ResponseEntity<Void> start(#RequestParam String clientId) {
//either start the task on app startup or start it here if not started
myFlagService.enableScheduler(clientId);
return new ResponseEntity<Void>(HttpStatus.OK);
}
#RequestMapping("stop")
ResponseEntity<Void> stop(#RequestParam String clientId) {
myFlagService.disableScheduler(clientId);
return new ResponseEntity<Void>(HttpStatus.OK);
}
//the scheduled task
private void myWorkerFunction() {
if (!myFlagService.isSchedulerEnabled(clientId)) {
return;
}
// do work
}

Related

Java + spring - run actions in infinite loop with random interval

I need to simulate a widget that shows purchases in real time.
To increase statistics, I want to supercharge real purchases with fake data, that needs to be emit in random interval.
All the events (real and fake ones) go to the message channel and get processed and then send to frontend.
So I need to come up with some service, that I can control (run and stop)
public class FakeDataGenerator {
private boolean run;
private Queue queue;
public void run() {
run = true;
while(run) {
queue.push(generateFakeOne())
TimeUnit.SECONDS.sleep(ThreadLocalRandom.current().nextInt(1, 30));
}
}
public void stop() {
run = false;
}
private Purchase generateFakeOne() {
// ... some faking logic
}
}
generator.stop();
where generator.run(); will start emitting events with random interval, and generator.stop(); will allow me to stop it any time
Is there any tool to accomplish such task? I'm not happy with using an infinite loop.
If you are using spring-boot why don't try using just #Scheduled(fixedDelay = 1000) annotation example
Example
#Scheduled(fixedDelay = 1000)
public void scheduleFixedDelayTask() {
System.out.println(
"Fixed delay task - " + System.currentTimeMillis() / 1000);
}
Use some of Spring Executor and give it a task to execute. You can control it by the executor reference

How to schedule task and get the response asynchronously?

I don't know if I've used the correct title, but I will try to explain the problem below.
I have the following method to check the payment status from external service:
#Autowired
PaymentService paymentService;
public void checkPaymentStatus(Payment p){
// sending
String status = paymentService.getPaymentStatus(p.getId());
}
It works in main thread and there might be hundreds of requests per second, so I decided to use CompletableFuture to run the tasks asynchronously in separate threads.
#Autowired
PaymentService paymentService;
public void checkPaymentStatus(Payment p){
// sending
CompletableFuture<Response> status = paymentService.getPaymentStatus(p.getId());
}
PaymentService.class
#Service
public class PaymentService{
private final RestTemplate restTemplate;
#Async
public CompletableFuture<Response> getPaymentStatus(long id){
Response results = restTemplate.getForObject(url, Response.class);
return CompletableFuture.completedFuture(results);
}
}
Configuration
#Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("payment-service-");
executor.initialize();
return executor;
}
It works perfectly, but I have another task here. Every request must be wait 30 seconds before sending to the external service.
How to solve this problem?
Update:
Update:
In this methods, I might use Thread sleep, but it is not the correct solution as it blocks the Thread for 30 seconds and next task might run after 60 secs, etc.
#Async
public CompletableFuture<Response> getPaymentStatus(long id){
// I might use Thread sleep here, but it is not the correct solution as it blocks the Thread for 30 seconds and next task might run after 60 secs, etc.
Response results = restTemplate.getForObject(url, Response.class);
return CompletableFuture.completedFuture(results);
}

thread is not asynchronous java

i have a spring boot aplication and i want send email with javamail using ses on aws. but if I send an email, while it and sent no other process is executed.
I want to send the email through a thread, but I've implemented a thread in this way and even then the email sending process is not asynchronous.
when I make this request to send email and then list all to see how the processing is, as long as the sending of the email does not finish the list request is not executed
#GetMapping
public ResponseEntity<?> listarUsuarios(){
System.out.println("--------begin send mail------------");
new SendMail(emailService).run();
System.out.println("--------finish send mail------------");
List<Usuario> usuariosList = usuarioRepository.findAll(); // <- this process not is processed when send email not finish
return new ResponseEntity<>(usuariosList,HttpStatus.OK);
}
.
public class SendMail extends Thread {
public EmailService emailService;
public SendMail(EmailService emailService) {
this.emailService = emailService;
}
public void run(){
try {
emailService.EnviarEmailDeConfirmacao("daviresio#gmail.com", 1, "Subject test mail","body test mail");
} catch (Exception e) {
e.printStackTrace();
}
}
}
You are not starting a new thread. Instead, you are calling the run() method directly:
new SendMail(emailService).run();
Call start() instead to start a new thread:
new SendMail(emailService).start();
By the way, starting new threads like this from a web application is bad practice. It's better to use for example an ExecutorService to manage the threads that send e-mails, so that you don't get a potentially unlimited number of threads when many users are calling this functionality at the same time.
You should use the start() method to spawn as a new thread. If you call run() directly it is run in the same thread. See https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html
Use start() instead of run().
Run will execute it on the existing thread.
Start will execute it on a new thread.
So change your code to the following if you want it to execute asynchronous:
new SendMail(emailService).start();
new SendMail(emailService).start(); - will start a new Thread and will execute SendMail.run(); in the new Thread.
new SendMail(emailService).run(); - is just a method call which executed in the same thread.

Recover an Asynch ThreadPoolTaskexecutor after server crashed/shut down

I Have a Spring rest controller which is calling an asynchronous method using Spring's #Async methodology and return immediately an http 202 code (Accepted) to the client.(The asynchronous job is heavy and could lead to a timeout).
So actually, at the end of the asynchronous task, i'm sending an email to the client telling him the status of his request.
Everything works just fine but I'm asking myself what can I do if my server/jvm crashes or if it is shut down? My client would receive a 202 code and will never receive a the status email.
Is there a way to synchronize (in real time) a ThreadPoolTaskExecutor in a database or even in a file to let the server recover at startup without managing this on my own with complex rules and evolution status?
Here is my Executor configuration
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("asyncTaskExecutor-");
executor.setAwaitTerminationSeconds(120);
executor.setKeepAliveSeconds(30);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
The controller that launch the async task
#RequestMapping(value = "/testAsync", method = RequestMethod.GET)
public void testAsync() throws InterruptedException{
businessService.doHeavyThings();
}
The async method called:
#Async
public void doHeavyThings() throws InterruptedException {
LOGGER.error("Start doHeavyThings with configured executor - " + Thread.currentThread().getName() + " at " + new Date());
Thread.sleep(5000L);
LOGGER.error("Stop doHeavyThings with configured executor - " + Thread.currentThread().getName() + " at " + new Date());
}
}
Thx
For a web server shutdown the application lifecycle in a java web application will notifiy a ServletContextListener. If you provide an implementation of a ServletContextListener you can put your 'what is already processed' logic in the contextDestroyed method.
When the web server or the application is started again the listener can be used to recover an re-process the unprocessed items of your job using the contextInitialized method.
Another option would be using Spring destruction callbacks and place the logic here.
HTH

How to synchronize parallel processes into a web service?

I need to develop a web service operation with CXF 3 hosted by Tomcat 7. Our model layer is Spring 3.
This operation calls 16 other web services hosted by distant servers. We need to wait all responses in order to construct the response of our own operation.
We currently call each distant operations sequentially. Of course, we have response time issue. I think we should parallelize our operation inner calls and synchronize the different responses.
What kind of multithreading implementation can be safe? What can we do to make it better?
I'd use Java's generic Futures and a Spring's #Async methods in a #Service.
In short, you call the services sequentially and get all results as Futures, and then you simply check whether all the futures have finished proccessing. You can also do some work with partial data if there is such possibility.
Here's a simple example on how to do it. A sample service from the link:
#Service
public class GitHubLookupService {
RestTemplate restTemplate = new RestTemplate();
#Async
public Future<User> findUser(String user) throws InterruptedException {
System.out.println("Looking up " + user);
User results = restTemplate.getForObject("https://api.github.com/users/" + user, User.class);
// Artificial delay of 1s for demonstration purposes
Thread.sleep(1000L);
return new AsyncResult<User>(results);
}
}
And a method using it:
#Override
public void run(String... args) throws Exception {
// Start the clock
long start = System.currentTimeMillis();
// Kick of multiple, asynchronous lookups
Future<User> page1 = gitHubLookupService.findUser("PivotalSoftware");
Future<User> page2 = gitHubLookupService.findUser("CloudFoundry");
Future<User> page3 = gitHubLookupService.findUser("Spring-Projects");
// Wait until they are all done
while (!(page1.isDone() && page2.isDone() && page3.isDone())) {
Thread.sleep(10); //10-millisecond pause between each check
}
// Print results, including elapsed time
System.out.println("Elapsed time: " + (System.currentTimeMillis() - start));
System.out.println(page1.get());
System.out.println(page2.get());
System.out.println(page3.get());
}
I would use a traditional approach using join() to wait for the threads to finish instead of polling (I don't like polling pattern too much).
Kind of this for a generic thread to replicate:
public class ThreadedWebServiceRetrieval extends Thread {
private List<ResultObject> resultList;
private GenericWebServiceStub stub;
public ThreadedWebServiceRetrieval (List<ResultObject> resultList, GenericWebServiceStub stub) {
this.resultList = resultList;
this.stub = stub;
}
public void run() {
resultList.add(stub.retrieveData());
}
}
And this for the parallel retrieval code:
// ... Controller/Service stuff
List<ResultObject> resultList = new LinkedList<>();//Diamond operator
List<Thread> webServiceList = new LinkedList<>();
webServiceList.add(new ThreadedWebServiceRetrieval(resultList, stub1));
//...
webServiceList.add(new ThreadedWebServiceRetrieval(resultList, stubN));
for (Thread thread : webServiceList) {
thread.start();
}
for (Thread thread : webServiceList) {
thread.join();
}
// resultList is fulfilled
Time of this approach should be +/- longest retrieval.
I made the code VERY generic (overall in the Thread implementation) but it's intentional to fit most cases.
Enjoy!

Categories

Resources