I've the problem, that I want to create a scheduled task during runtime. The scheduled task should be triggered with a fixed rate. But now I'm having the problem that the manual setup schedules are not triggered in an async way.
The main problem is, that we do not have any fix point were we can start the scheduler. It should get created when I read a specific value (1) and gets destroyed when the value changes back (0). Otherwise we could use the annotation configuration described in test 1 below.
What I have tried so far:
1. Schedule with #Scheduled(fixedRate = 500L) and #Async
Code
#Async
#Scheduled(fixedRate = 500L)
public void annotationTest() {
UUID id = UUID.randomUUID();
log.warn("Hello from Thread {} going to sleep", id);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.warn("Finished Thread {}", id);
}
Also having the #EnableAsync and #EnableScheduling annotations on class level.
Result
09:56:24.855 [task-5] : Hello from Thread 3b5514b2-3b80-4641-bf12-2cd320c4b6e5 going to sleep
09:56:25.355 [task-6] : Hello from Thread e98514a7-e193-422b-9569-f7635deb33f8 going to sleep
09:56:25.356 [task-4] : Finished Thread d86f5f24-bffb-4ddd-93fe-2334ed48cf91
09:56:25.854 [task-7] : Hello from Thread cfc2ab03-4e7e-4a4a-aa08-41d696cb6df7 going to sleep
09:56:25.855 [task-5] : Finished Thread 3b5514b2-3b80-4641-bf12-2cd320c4b6e5
09:56:26.355 [task-6] : Finished Thread e98514a7-e193-422b-9569-f7635deb33f8
Comment
This works as expected, but we are not able to use it, because we have to create the scheduler during runtime and destroy it after a specific time/input.
2. Setting up a ScheduledTaskRegistrar
Code
//#Configuration
#Bean
public ScheduledTaskRegistrar scheduledTaskRegistrar() {
ScheduledTaskRegistrar scheduledTaskRegistrar = new ScheduledTaskRegistrar();
scheduledTaskRegistrar.setScheduler(threadPoolTaskScheduler());
return scheduledTaskRegistrar;
}
#Bean
public TaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20);
return scheduler;
}
//#Component
public void printMessages() {
scheduledTaskRegistrar.scheduleFixedRateTask(new FixedRateTask(new OwnRunnable(), 500L, 0L));
}
The OwnRunnable will also sleep 1 second and print the finish Text afterwards
Result
10:13:56.983 [TaskScheduler-1] : Finished Thread 73f70de9-35d9-47f0-801b-fb2857ab1c34
10:13:56.984 [TaskScheduler-3] : Hello from Thread 7ab16380-8dba-49e1-bf0d-de8235f81195 going to sleep
10:13:57.984 [TaskScheduler-3] : Finished Thread 7ab16380-8dba-49e1-bf0d-de8235f81195
10:13:57.984 [TaskScheduler-2] : Hello from Thread cc152d2e-f93b-4770-ac55-853a4dd6be97 going to sleep
10:13:58.985 [TaskScheduler-2] : Finished Thread cc152d2e-f93b-4770-ac55-853a4dd6be97
10:13:58.985 [TaskScheduler-4] : Hello from Thread 8d4510a4-773d-49f3-b51b-e58e425b0b68 going to sleep
Comment
As we can see the tasks run in a synchronous way and will not fit to our requirement.
3. Other tests
All other tests are similar to the test described in 2 but will use some other configurations of the ScheduledTaskRegistrar. The results are the same as in test 2.
ConcurrentTaskScheduler instead of ThreadPoolTaskScheduler
ConcurrentTaskScheduler with SimpleAsyncTaskExecutor as ConcurrentExecutor
ConcurrentTaskScheduler with ThreadPoolTaskExecutor as ConcurrentExecutor
Question(s)
How can I use the configuration described in test 2 but get the result of test 1? Is there a way to use the #Async annotation with solution described in test 2? Or does anyone have a better/ another solution for my problem?
Yes, it is possible. Assume that your class that implemented SchedulingConfigurer has a method, doMyJob(). You can annotate that method with Async and use the reference in FixedRateTask. Also notice the class level annotation
#Configuration
#EnableAsync
public class MyJobConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.scheduleFixedRateTask(new FixedRateTask(this::doMyJob, 500L, 0L));
}
#Async
public void doMyJob() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Hope it helps
EDIT
I provided the code without testing. Recently when I tried to recreate this scenario, I noticed that if doMyJob is within SchedulingConfigurer, it will not be truly async (if delay is 5seconds and job takes 10seconds, next job runs only after 10seconds). But moving the method to a service class helped.
Related
Could somebody help me to do the following:
#PostContruct public void func() {
webclient.get()...subscribe();
}
webclient call will terminate after func() returns. Most likely it will happen before the first request comes in, but no guarantees. The other option is to block(), which defeats the purpose of being reactive.
What would be the right way to make reactive calls in #PostConstruct methods?
Thank you.
I created a simple bean.
Synchronous update:
#Component
public class BeanTest {
private String postConstructValue;
#PostConstruct
public void init(){
try {
Thread.sleep(5000);
this.postConstructValue = "Construction done";
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Scheduled(fixedRate = 500)
public void print(){
System.out.println(
this.postConstructValue
);
}
}
It took some time for app to start (more than 5 seconds) because we simulated some time consuming process in the post construct. Scheduled print method started printing only after the app started. It started printing "Construction done" message.
Asynchronous update:
#Component
public class BeanTest {
private String postConstructValue;
#PostConstruct
public void init(){
Flux.just("Construction done")
.delayElements(Duration.ofSeconds(5))
.subscribe(s -> this.postConstructValue = s);
}
#Scheduled(fixedRate = 500)
public void print(){
System.out.println(
this.postConstructValue
);
}
}
Now in this approach, the app started within 2 seconds. Print method starting printing null for few seconds. Then it started printing "Construction done". It does not terminate the Flux postConstruct value update. It happened asynchronously.
Reactive approach is good when you want a non-blocking behavior and getting something done asynchronously. If you think that your component creation should wait for proper construction, you have to block! Otherwise, you can go with second approach.
In my Spring application, there is a scheduler for executing some task. Scheduled annotation is not used there because the schedule is quite complicated - it is dynamic and it used some data from the database. So simple endless cycle with thread sleeping is used. And sleeping interval is changed according to some rules. Maybe all this can be done with Scheduled annotation, but the question is not about that.
Below is simple example:
#Service
public class SomeService {
#PostConstruct
void init() {
new Thread(() -> {
while (true) {
System.out.println(new Date());
try {
Thread.sleep(1000);
} catch (Exception ex) {
System.out.println("end");
return;
}
}
}).start();
}
}
The code works fine but there is some trouble with killing that new thread. When I stop the application from Tomcat this new thread is continuing to run. So on Tomcat manage page I see that application is stopped, but in Tomcat log files I still see the output from the thread.
So what the problem? How I should change the code so the thread would be killed when the application is stopped?
Have you tried to implement a #PreDestroy method which will be invoked before WebApplicationContext is closed to change a boolean flag used in your loop? Though it seems strange that your objects are not discarded even when application is stopped...
class Scheduler {
private AtomicBoolean booleanFlag = new AtomicBoolean(true);
#PostConstruct
private void init() {
new Thread(() -> {
while (booleanFlag.get()) {
// do whatever you want
}
}).start();
}
#PreDestroy
private void destroy() {
booleanFlag.set(false);
}
}
Every 5 minutes, 2 tasks run, one that updates, one that calculates.
The one that updates should always run first, but doesn't have to know anything about the calculator and visa versa.
Is there any way to manage this in a clean way without calling the other task from within.
After advice, this is my solution.
#PostConstruct
public void scheduleRunnableWithTrigger() {
logger.info("init scheduler");
scheduleTasks.schedule(() -> {
if(init.getIsInitialzed()) {
databaseUpdater.updatePrices();
for (ITechnicalAnalysis technicalAnalysis: adviser.getITechnicalAnalysis()) {
try {
CandleStick lastAddedCandleStickByCurrencyPair = candleStickService.getLatestByCurrencyPair(technicalAnalysis.getCurrencyPair());
technicalAnalysis.updateAlgorithms(lastAddedCandleStickByCurrencyPair);
logger.info(technicalAnalysis.getCurrencyPair() + " has candlesticks");
}catch (NullPointerException e) {
logger.warn(technicalAnalysis.getCurrencyPair() + " has no candlesticks");
}
}
adviser.calculateAnalyses();
}
}, periodicTrigger);
}
#Bean
public PeriodicTrigger periodicTrigger() {
PeriodicTrigger periodicTrigger = new PeriodicTrigger(TIME_TO_WAIT_FOR_DATABASE_TO_FILL, TimeUnit.MINUTES);
periodicTrigger.setInitialDelay(1);
return periodicTrigger;
}
According to the documentation. Be default there will be only one thread for scheduler.
So what you can do is you can schedule the tasks one after the other may be a second apart. This way second task will always run after first task.
Even if first task is not completed and schedule time for second task has reached, second task will have to wait for the thread from first task to be released.
You can also explicitly set the thread count for the scheduler with a bean declaration like this
#Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(THREAD_COUNT);// use 1 for this use case
return threadPoolTaskScheduler;
}
I have a very simplified code sample below, but basically I have a scheduler that calls a controller on a new thread. In that new thread MOST autowired beans work fine, but one of them, MyController, just hangs if i try and invoke it for any reason. The only thing i can find different about MyController to the other autowired objects is that it is 'EnhancerbyspringCGLIB' which I suspect is the problem. I must be doing something stupid or losing context by creating a new thread and it then cannot resolve a proxy or something? I am lost though so any pointers would be appreciated!
Note, if I call the method directly instead of from a new thread all works fine, but I cannot do this as its test code which bypasses event handling and I need to make sure there is no stale java objects in memory.
Sample code:
#Primary
#Component
public class Scheduler {
#Autowired MyController controller;
void myMethod(){
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
controller.doSomething();
});
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Controller
public class MyController{
doSomething(){
//I will never get here
}
}
I want to use camel->quartz component to schedule some job to be done at specified time interval.
But I want that in synchronized manner. Means, Next execution of scheduled job should only start after completion of current execution.
I created Route and Scheduler Service for Servicemix.
QuartzRoute.java
public class QuartzRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("quartz://myGroup/myTimerName?cron=0/1+*+*+*+*+?").process(new SchedulerService());
}
}
SchedulerService.java
public class SchedulerService implements Processor {
public void process(Exchange exchange) throws Exception {
System.out.println("I'm running every 5 sec...");
Thread.sleep(5000);
System.out.println("Exiting iteration ");
}
}
Here, I want "I'm running every 5 sec..." and "Exiting iteration " to be printed in same order every time.
In sort i want this SchedulerService to be executed again only after completion of current execution.
Use the stateful=true option of the quartz component. See Scheduled with fixed delay in quartz scheduler?
"stateful jobs are not allowed to execute concurrently, which means new triggers that occur before the completion of the execute(xx) method will be delayed."