I Need to schedule a task on Spring Boot that reads a cron espression from the database. I did this using the #Scheduled annotation and reading a property inside a database, but my client is asking to be able to update the cron expression in the database and having it affect the scheduled without restarting the application. I know this isnt possible with the #Scheduled annotation, but would It be possible to schedule another task that extracts the cron expression every hour, and then feed the updated expression to the actual scheduled that executes the task? Basically updating the variable that Is Fed to the second scheduled. If this isnt possible, do you know any alternative ways to achieve this without using the #Scheduled annotation? Thank you.
You could try doing this using your own Runnable and a ScheduledExecutorService Which starts a thread to do what you are asking once every hour.
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
public void chronJob Runner() {
final Runnable chronJobWorker = new Runnable() {
public void run() { //Request logic }
};
scheduler.scheduleAtFixedRate(beeper, 1, 60, TimeUnit.MINUTES);
Not sure if this is the best way of doing it, but is certainly one possible way of completing this task at a scheduled rate.
Solved this using SchedulingConfigurer, here's a sample:
#Configuration
#EnableScheduling
public class BatchConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() {
#Override
public void run() {
//run your code here
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
//extract cron from database
CronTrigger trigger = new CronTrigger(new CronTrigger(//insert cron that you got from database));
return trigger.nextExecutionTime(triggerContext);
}
});
}
}
Related
I want to execute the same task couple of times but it seems that every next invocation of my code does not execute the task immediately, for example it executes after one minute.
Since user has to schedule tasks manually I use ScheduledTaskRegistrar.TaskScheduler.
taskRegistrar.getScheduler().schedule(myTask, new Date());
What could be the reason? User clicked schedule button twice on my fronted application and backend invoked the above schedule method twice as expected. First execution of my task was immediate, second run after two minutes.
UPDATE: taskregistrar config, maybe I didn't configure it at all. my tasks are added as cron tasks on application deployment. But they also must be runnable manually if user wants to trigger it. Below is more or less the whole logic:
#Configuration
#EnableAsync
#EnableScheduling
#Component
#Slf4j
#Generated
#Getter
public class ScheduleTaskService implements SchedulingConfigurer {
#Autowired
private List< MyTask> taskList;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
taskList.stream().filter(MyTask::isOn).forEach(this::addTaskToScheduler);
}
public void addTaskToScheduler(GwoTask task) {
taskRegistrar.addCronTask(task, task.getCronExpression());
}
public void scheduleImmediateInvocation(MyTask myTask) {
taskRegistrar.getScheduler().schedule(myTask, new Date());
}
}
By referring to the source code of ScheduledTaskRegistrar,
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
...
If we do not set taskScheduler, Executors.newSingleThreadScheduledExecutor() is used by default. Hence new task will be blocked by processing task.
For your use case in scheduleImmediateInvocation, I recommend to use another thread pool(Probably from Executors) instead as:
It isn't actually a schedule job.
More control on pool size is needed to suit your workload
If you just want to make ScheduledTaskRegistrar execute more concurrently, configure it as:
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// set the desired core pool size
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
this.taskRegistrar = taskRegistrar;
// ...
}
I am new to spring boot and I have a requirement in which I have to run scheduler only if new data is inserted into table. Thanks for any help
Hibernate has an interceptor mecanism that allows you to get notified, at specific times, when database events occurs.
Such events are creation/deletion/flush of the session. As you get access to the objects being subject to the given event, you have a mean to fire a process when a given object of a given class (which you can easily map to a table in your schema) is modified.
The javadoc can be found here :
https://docs.jboss.org/hibernate/orm/4.0/manual/en-US/html/events.html
You can use the interceptor mecanism along with Java's ScheduledExecutorService to schedule a task when hibernate intercepts the save operation. You can create your business logic under that interceptor.
Scheduling is not enabled by default. Before adding any scheduled jobs we need to enable scheduling explicitly by adding the #enableScheduling annotation.
Now with the help of the ScheduledTaskRegistrar#addTriggerTask method, we can add a Runnable task and a Trigger implementation to recalculate the next execution time after the end of each execution.
#EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {
#Autowired
private TickService tickService;
#Bean
public Executor taskExecutor() {
return Executors.newSingleThreadScheduledExecutor();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
#Override
public void run() {
tickService.tick();
}
},
new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext context) {
Optional<Date> lastCompletionTime =
Optional.ofNullable(context.lastCompletionTime());
Instant nextExecutionTime =
lastCompletionTime.orElseGet(Date::new).toInstant()
.plusMillis(tickService.getDelay());
return Date.from(nextExecutionTime);
}
}
)
}
}
I am looking for a scheduler library which can perform a simple task of invoking other REST APIs at a specific time of a day. Please advise on which is a good library to use in a Spring Boot project. I am basically looking for something which logs job config information to DB automatically and has a UI interface to check the status of jobs (preferable but not mandatory).
I did come across this but as I have no prior experience with any of them but Quartz so I am not able to make the call: http://blog.dreamcss.com/tools/java-based-job-scheduler/
Note: I did use Quartz in my previous project but I ran into multiple issue with it as it seems to have issues with not logging job related info to DB. Specifically it would not log proper info into DB about last run time and whether the last job run completed successfully or not. Also, I have seen that the Jobs in Quartz gets blocked if the previous job takes longer to complete.
In spring boot you have an embedded simple engine for scheduling.
Use #Scheduled annotation in your #Component for example.
And don't remember to enable scheduling by using #EnableScheduling annotation.
You can read more about this topic in this article spring.io link
Using a Trigger you can calculate the next execution time on the fly.
Something like this should do the trick (adapted from the Javadoc for #EnableScheduling):
#Configuration
#EnableScheduling
public class MyAppConfig implements SchedulingConfigurer {
#Autowired
Environment env;
#Bean
public MyBean myBean() {
return new MyBean();
}
#Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
#Override public void run() {
myBean().getSchedule();
}
},
new Trigger() {
#Override public Date nextExecutionTime(TriggerContext triggerContext) {
Calendar nextExecutionTime = new GregorianCalendar();
Date lastActualExecutionTime = triggerContext.lastActualExecutionTime();
nextExecutionTime.setTime(lastActualExecutionTime != null ? lastActualExecutionTime : new Date());
nextExecutionTime.add(Calendar.MILLISECOND, env.getProperty("myRate", Integer.class)); //you can get the value from wherever you want
return nextExecutionTime.getTime();
}
}
);
}
}
I am building a Spring 4 Rest API for a trade automation site.
An http request will contain some info along with a date-time. After inserting these info into database (using hibernate), I need to dynamically create a new cron job which will access these db info and do something. The cron job must be executed at the time specified above.
So there wont be a fixed cron expression, also the cron task must access my DAO layer annoted with #Repository.
Even after referring a lot of post in stack and other blog, which tells about #Scheduled, Spring-Quartz integration, I couldn't find out a solution for my specific need.
Java/Annotation configuration is preferred.
Please help.
Thanks
You can use Trigger and TaskScheduler interfaces. Example below. To cancel job in the future it will be needed to store ScheduledFuture instance.
#Configuration
public class AppConfiguration {
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
}
#Controller
public class TriggerService {
#Autowired
private TaskScheduler scheduler;
#Autowired
private DAOService db;
private ScheduledFuture job;
#GetMapping("/task1")
#ResponseBody
public void triggerMyTask(#RequestParam String cronExpression) {
Runnable runnable = new Runnable() {
#Override
public void run() {
log.info(new Date());
/**
* here You can do what You want with db
* using some DAOService
*/
db.count();
}
};
/**
* cancel current task if You need
*/
if(job != null) {
job.cancel(true);
}
CronTrigger trigger = new CronTrigger(cronExpression);
job = scheduler.schedule(runnable, trigger);
}
}
You can pass cron expression for example like that:
http://localhost:8080/task1?cronExpression=0/5%20*%20*%20*%20*%20*
I think you may use something like this: https://ha-jdbc.github.io/apidocs/net/sf/hajdbc/util/concurrent/cron/CronThreadPoolExecutor.html
I'm trying to implement a system in server that will do some updates on database on a ragular basis.
Here: Spawning threads in a JSF managed bean for scheduled tasks using a timer
and in some other similar questions, I saw that BalusC strongly recommended to use Stateless Beans, if not possible SchedulerExecuterService rather than Timer.
Here is my situation. I need a JSF page in which I can configure the schedule interval. i.e. I can change its rule from run once in every 5 min to run once in 10 min
First, I tried to use #Schedule annotation and it was great. However, I couldn't find a way to change interval with that. First question, is it possible to change it dynamically like I told above?
I am currently using SchedulerExecutorService which is called from #PostConstruct of a Stateless Bean.
Second question, is the Timer BalusC strongly recommended not to use is the TimerService of EJB?
Third Question, I liked the properties of timerService which is:
using scheduleExpression and timerConfig.
Are there any similar things for ScheduledExecutorService?
Additional Question: Am I on right track? Can the thing that I am trying to pull be done in a better way?
I think #Schedule is used only for fixed cron-like timers, where the EJB container deploys a timer at EJB startup. You obviously need more dynamic scheduling connected with a JSF page.
If you are running on a full Java EE 6 profile, why not use the TimerService with a Stateless Session EJB like this:
#Stateless
public class JobSchedulerBean {
#Resource
private TimerService timerService;
// #PostConstruct
public void initTimer() {
// set initial timer
ScheduleExpression sch = new ScheduleExpression();
// set cron expression into sch
timerService.createCalendarTimer(sch, new TimerConfig("myTimer", false));
}
public void rescheduleTimer(int interval) {
// get timer from timer service
for (Timer timer : timerService.getTimers()) {
if (timer != null && timer.getInfo().equals("myTimer")) {
timer.cancel();
}
}
// schedule new timer, like in initTimer() method
}
#Timeout
public void timeout(Timer timer) {
// do the job
}
}
EDIT:
#ManagedBean(eager=true)
#ApplicationScoped
public class JobRunner {
private ScheduledExecutorService scheduler;
private static final int POOL_SIZE = 1;
ScheduledFuture<?> runHandle;
#PostConstruct
public void init() {
scheduler = Executors.newScheduledThreadPool(POOL_SIZE);
// set initial expiry to 5 minutes after 5 minutes delay
runHandle = scheduler.scheduleAtFixedRate(new MyJob(), 5, 5, TimeUnit.MINUTES);
}
#PreDestroy
public void destroy() {
scheduler.shutdownNow();
}
public void reschedule(int newDelay) {
// cancel old timer, but do not interrupt it
runHandle.cancel(false);
runHandle = scheduler.scheduleAtFixedRate(new MyJob(), newDelay, newDelay, TimeUnit.MINUTES);
}
}
public class MyJob implements Runnable {
public void run() {
// do the job
}
}