Can the fixed delay of a #Scheduled task be configured at runtime - spring

I have a database poller that I have implemented as a scheduled task. I am using Spring 3.0. I want to control the fixed-delay of the scheduled task at runtime using a restful api. Is there a way to achieve this in spring?
as of now i have:
#Scheduled(fixedDelay = 30000)
public void poll() {
if (pollingEnabled) {
// code to do stuff in database
}
}
I want the fixedDelay to be dynamic.Thanks.

Related

Sprint boot async - does not use max-size

I am trying out Spring Boot's Async feature, but I am having some trouble getting it to work as I need.
This is my application yml
spring:
task:
execution:
pool:
max-size: 100
queue-capacity: 5
keep-alive: "10s"
core-size: 10
Application class
#SpringBootApplication
#EnableAsync
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
Service class:
for (int i=0;i< 40; i++) {
CompletableFuture.runAsync(()-> {
try {
System.out.println("------------------Starting thread------------------");
//do some action here
System.out.println("------------------Ending thread------------------");
} catch (Exception e) {
e.printStackTrace();
}
});
}
I am expecting to see the System.out print out 40 times. The operations in between take long enough, and I have tried adding Thread.sleep(), but I do not see the sysouts printed more than 8 times. Is there something wrong with my config, or does it not work the way I expect?
Completable future has no idea about the pool that is used by Spring.
From docs of runAsync() method:
Returns a new CompletableFuture that is asynchronously completed by a
task running in the ForkJoinPool.commonPool() after it runs the given
action. Params: runnable – the action to run before completing the
returned CompletableFuture Returns: the new CompletableFuture
So, those tasks are being run on ForkJoinPool, not on executor used by Spring.
About executor used by Spring with #EnableAsync:
By default, Spring will be searching for an associated thread pool
definition: either a unique TaskExecutor bean in the context, or an
Executor bean named "taskExecutor" otherwise. If neither of the two is
resolvable, a SimpleAsyncTaskExecutor will be used to process async
method invocations. Besides, annotated methods having a void return
type cannot transmit any exception back to the caller. By default,
such uncaught exceptions are only logged.
You could try autowire that executor and pass it as an argument to
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
Returns a new CompletableFuture that is asynchronously completed by a
task running in the given executor after it runs the given action.
Params: runnable – the action to run before completing the returned
CompletableFuture executor – the executor to use for asynchronous
execution

Programmatically trigger a #Scheduled method?

I'm working on a Springboot app that includes a task that's executed on a schedule. It typically takes about two to three minutes to run.
#Scheduled(cron = "* */30 * * * *")
public void stageOfferUpdates() throws SQLException {
...
}
We have a requirement to be able to kick off the execution of that task at any time by calling a rest endpoint. Is there a way my #GET method can programmatically kick this off and immediately return an http 200 OK?
So you just want to trigger an async task without waiting for results. Because you are using Spring, the #Async annotation is an easy way to achieve the goal.
#Async
public void asyncTask() {
stageOfferUpdates();
}
Couldn't you just run the method in another Thread:
executor.execute(() -> {
stageOfferUpdates();
}
and then procede and return 200?

Scheduling a function in Spring boot

I am trying to schedule the following function to run every 10seconds. But it tells that it needs a void function. Is there any way to schedule the following function?
#Scheduled(fixedRate = 10000)
public FtpOutboundGateway getFiles() {
FtpOutboundGateway gateway = new FtpOutboundGateway(sf(), "mget", "payload");
gateway.setAutoCreateDirectory(true);
gateway.setLocalDirectory(new File("./downloads/"));
gateway.setFileExistsMode(FileExistsMode.REPLACE_IF_MODIFIED);
gateway.setFilter(new AcceptOnceFileListFilter<>());
gateway.setOutputChannelName("fileResults");
return gateway;
}
Since #Scheduled method is called internally by the threads in thread pool in a given interval or time, the annotation expect it to be void because it doesn't make sense to return values. if you want to track down the response the extract it to another method and call it from the #Scheduled method as well as from other services.
For this, we need to do the following things.
First put this Annotation #EnableScheduling at the class level.
then at the function which needs to be scheduled should be marked with #Scheduled.
The function that we are trying to schedule should not return anything.
#EnableScheduling
class <ClassName>{
//def
#Scheduled(fixedDelayString = "10000")
public void fxnName() {
//def
}
}

How to initialize/enable Bean after another process finishes?

The idea is that I would like to first let a #Scheduled method retrieve some data and only when that process has finished enable/initialize my #KafkaListener. Currently the Kafka listener starts up immediately without waiting for the scheduler to be done.
I've tried to use #Conditional with a custom Condition, but this only is executed on context creation (aka startup). Also #ConditionalOnBean didn't work because actually my Scheduler bean is already created before it finishes the process.
This is how my setup looks like.
Kafka Listener:
#Service
class KafkaMessageHandler(private val someRepository) {
#KafkaListener(topics = ["myTopic"])
fun listen(messages: List<ConsumerRecord<*, *>>) {
// filter messages based on data in someRepository
// Do fancy stuff
}
}
Scheduler:
#Component
class Scheduler(private val someRepository) {
#Scheduled(fixedDelayString = "\${schedule.delay}")
fun updateData() {
// Fetch data from API
// update someRepository with this data
}
}
Is there any nice Spring way of waiting for the scheduler to finish before initializing the KafkaMessageHandler?

Activiti Escalation Listener Configuration

I am using activiti 5.18.
Behind the scenes : There are few task which are getting routed though a workflow. Some of these tasks are eligible for escalation. I have written my escalation listener as follows.
#Component
public class EscalationTimerListener implements ExecutionListener {
#Autowired
ExceptionWorkflowService exceptionWorkflowService;
#Override
public void notify(DelegateExecution execution) throws Exception {
//Process the escalated tasks here
this.exceptionWorkflowService.escalateWorkflowTask(execution);
}
}
Now when I start my tomcat server activiti framework internally calls the listener even before my entire spring context is loaded. Hence exceptionWorkflowService is null (since spring hasn't inejcted it yet!) and my code breaks.
Note : this scenario only occurs if my server isn't running at the escalation time of tasks and I start/restart my server post this time. If my server is already running during escalation time then the process runs smoothly. Because when server started it had injected the service and my listener has triggered later.
I have tried delaying activiti configuration using #DependsOn annotation so that it loads after ExceptionWorkflowService is initialized as below.
#Bean
#DependsOn({ "dataSource", "transactionManager","exceptionWorkflowService" })
public SpringProcessEngineConfiguration getConfiguration() {
final SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setAsyncExecutorActivate(true);
config.setJobExecutorActivate(true);
config.setDataSource(this.dataSource);
config.setTransactionManager(this.transactionManager);
config.setDatabaseSchemaUpdate(this.schemaUpdate);
config.setHistory(this.history);
config.setTransactionsExternallyManaged(this.transactionsExternallyManaged);
config.setDatabaseType(this.dbType);
// Async Job Executor
final DefaultAsyncJobExecutor asyncExecutor = new DefaultAsyncJobExecutor();
asyncExecutor.setCorePoolSize(2);
asyncExecutor.setMaxPoolSize(50);
asyncExecutor.setQueueSize(100);
config.setAsyncExecutor(asyncExecutor);
return config;
}
But this gives circular reference error.
I have also tried adding a bean to SpringProcessEngineConfiguration as below.
Map<Object, Object> beanObjectMap = new HashMap<>();
beanObjectMap.put("exceptionWorkflowService", new ExceptionWorkflowServiceImpl());
config.setBeans(beanObjectMap);
and the access the same in my listener as :
Map<Object, Object> registeredBeans = Context.getProcessEngineConfiguration().getBeans();
ExceptionWorkflowService exceptionWorkflowService = (ExceptionWorkflowService) registeredBeans.get("exceptionWorkflowService");
exceptionWorkflowService.escalateWorkflowTask(execution);
This works but my repository has been autowired into my service which hasn't been initialized yet! So it again throws error in service layer :)
So is there a way that I can trigger escalation listeners only after my entire spring context is loaded?
Have you tried binding the class to ApplicationListener?
Not sure if it will work, but equally I'm not sure why your listener code is actually being executed on startup.
Try to set the implementation type of listeners using Java class or delegate expression and then in the class implement JavaDelegate instead of ExecutionListener.

Resources