Allow the end user to schedule task with Spring Boot - spring-boot

I'm using Spring Boot and I want to allow the end user to schedule task as he wants.
I have a Spring Boot REST backend with a Angular frontend. The user should be able to schedule (with a crontab style syntax) a task (i.e. a class' method backend side), and choose some arguments.
The user should alse be able to view, edit, delete scheduled task from the front end
I know I can use #Scheduled annotation, but I don't see how the end user can schedule task with it. I also take a look at Quartz, but I don't see how to set the user's argument in my method call.
Should I use Spring Batch?

To schedule job programatically, you have the following options:
For simple cases you can have a look at ScheduledExecutorService, which can schedule commands to run after a given delay, or to execute periodically. It's in package java.util.concurrent and easy to use.
To schedule Crontab job dynamically, you can use quartz and here's official examples, basically what you'll do is:
Create instance Scheduler, which can be defined as a java bean and autowire by Spring, example code:
Create JobDetail
Create CronTrigger
Schedule the job with cron
Official example code(scheduled to run every 20 seconds):
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
JobDetail job = newJob(SimpleJob.class)
.withIdentity("job1", "group1")
.build();
CronTrigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(cronSchedule("0/20 * * * * ?"))
.build();
sched.scheduleJob(job, trigger);

Related

How to write a tenantaware RepositoryItemReader in Spring batch?

I have a job configured to run based on the job parameters and integrated with spring web and quartz to invoke based on demand and cron based. I am using RepositoryItemReader to take advantage of spring data. This is running as expected.
Now I want to introduce multi tenancy in the job. I have 3 tenants with different databases say tenant1, tenant2 and tenant3. Basically i want to run the batch job picking the data from the database based on the jobparameter. If the jobparameter is tenant1, i want to pick the data from the tenant1 database.
I have found an article on how to introduce multi tenancy in spring boot application here. https://www.baeldung.com/multitenancy-with-spring-data-jpa
The problem is that i am not able to understand where i could inject the context into the thread as i am using an AsyncTaskScheduler to launch a job and there are other jobs which are also registered in the context.
JobParameters jobParameters = new JobParametersBuilder()
.addString("tenantId",tenantId)
.addString("jobName",jobName)
.addLong("time", System.currentTimeMillis()).toJobParameters();
Job job = jobRegistry.getJob(jobName);
JobExecution jobExecution = asyncJobLauncher.run(job, jobParameters);
My itemReader bean is described as
#StepScope
#Bean
public ItemReader<Person> itemReader() {
return new RepositoryItemReaderBuilder<Person>()
.name("ItemReader")
.repository(personRepository)
.arguments("personName").methodName("findByPersonNameEquals")
.maxItemCount(30).pageSize(5)
.sorts(Collections.singletonMap("createTs", Sort.Direction.ASC)).build();
}
I discovered a work around for the problem by
Extending the RepositoryItemReader something like TenantAwareRepositoryItemReader which takes tenant as contructor arg.
Override the doPageRead() function in TenantAwareRepositoryItemReader by
Setting the tenantId in the threadcontext
Calling the super.doPageRead()
Clear the db thread context
Use the TenantAwareRepositoryItemReader as a itemReader.

how to prevent quartz from running another job if the previous one is still running?

I'm using Quarkus. My Quartz jobs are scheduled to run every 10 seconds:
return TriggerBuilder.newTrigger()
.withIdentity("my-job")
.startNow()
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever()
).build();
This works fine but jobs keep triggering every 10 seconds irrespective of whether or not the last one finishes. I need the next job to start only if there are no jobs currently running job. How do I accomplish this?
Add #DisallowConcurrentExecution on you Job class.
as example :
#DisallowConcurrentExecution
public class MyScheduledJob implements Job {
//execution method
}

How to dynamically schedule multiple tasks in Spring Boot

I want to dynamically schedule a task based on the user input in a given popup.
The user should be able to schedule multiple tasks and each tasks should be a repeteable task.
I have tried to follow some of the possibilities offered by spring boot using espmale below:
example 1: https://riteshshergill.medium.com/dynamic-task-scheduling-with-spring-boot-6197e66fec42
example 2: https://www.baeldung.com/spring-task-scheduler#threadpooltaskscheduler
The Idea of example 1 is to send a http post request that should then invoke a schudeled task as below :
Each http call will lead to console print as below :
But I still not able to reach the needed behaviour; what I get as result is the task1 executed when invoked by action1 but as soon as a task2 is executed by an action2 the first task1 will stop executing .
Any idea how the needed logic could be implemented?
Example 1 demonstrates how to schedule a task based on requested rest api and Example 2 shows how to create ThreadPoolTaskScheduler for TaskScheduler. But you miss an important point, here. Even if you created thread pool, TaskScheduler is not aware of that and thus, it needs to be configured so that it can use thread pool. For that, use SchedulingConfigurer interface. Here is an example:
#Configuration
#EnableScheduling
public class TaskConfigurer implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
//Create your ThreadPoolTaskScheduler here.
}
}
After creating such configuration class, everything should work fine.

Spring Scheduler stops working for my cron expression

I've a method scheduled to run periodically with Spring Scheduler, it's been working fine and stopped working today with no error. What could be the potential cause ? Is there any alternative way to schedule task periodically using Spring Scheduler that ensures that the method will be executed no matter what?
#Scheduled(cron="0 0/1 * * * ?")
public void executePollingFlows(){
if(applicationConfig.isScheduleEnabled()) {
for (long flowId : applicationConfig.getPollingFlowIds()) {
flowService.executeFlow(flowId);
}
logger.info("Finished executing all polling flows at {}", new Date());
}
}
You may have got Out of Memory exception if the job could not finish its tasks but you try to run it again and again. If it is a Out of Memory exception you may try to create a ThreadPool and check it in every run. If there is no enough space in the ThreadPool you can skip the task for this turn.
There is alternative way to use #Scheduled periodically. You may change your #Scheduled annotation with this:
#Scheduled(fixedRate=1000)
It will still be running in every second and if necessary you can add initialDelay to it:
#Scheduled(initialDelay=1000, fixedRate=1000)
You can find more details about fixedRate, initialDelay and fixedDelay here:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html

Reschedule the scheduled task in spring scheduler

I have a Runnable class which is scheduled to run at fixed rate (I am using spring scheduler):
taskScheduler.scheduleAtFixedRate(this, startTime.toDate(),
PERIOD * 1000);
Can I reschedule the task or change the period dynamically after it has been scheduled?
You can probably use the following DynamicPeriodicTrigger https://github.com/spring-projects/spring-integration-samples/blob/master/intermediate/dynamic-poller/src/main/java/org/springframework/integration/samples/poller/DynamicPeriodicTrigger.java
So you have to create a DynamicPeriodicTrigger instance (with fixedRate = true) and put the reference into taskScheduler.schedule(Runnable task, Trigger trigger); method.
You can than dynamically change object attributes, to change execution periods on the fly.

Resources