I have a method in my bean, which executes periodically:
#Scheduled(fixedRate = xx)
public void runPeriodically() {
// do smt...
}
Now I want to find out the time of its previous execution. How can I do that? I read about Trigger interface, but it's not clear to me how to use it for my need.
I might be missing something, but wouldn't a simple instance variable do the job?
private Date lastRun;
#Scheduled(fixedRate = xx)
public void runPeriodically() {
// do smt...
lastRun = new Date();
}
As for the Trigger interface: you can't use #Scheduled in combination with the Trigger interface. At least not out of the box. If you want to use Trigger, you need to use a TaskScheduler and "feed" it with Trigger objects. E.g.
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
CronTrigger obviously implements Trigger, so you have all your interface methods there.
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 and have only scratched the surface of what can be done with it.
I have a situation where I need to set up a recurring task using the #Scheduled annotation. The rate is specified as a member field in an object that is passed to the class encapsulating the method representing the task.
I've used the mechanism that allows for accessing the configuration or environment, e.g. #Scheduled(fixedRateString = "${some.property:default}"); this works great.
What I don't know how to do is insert the value from an object into the #Scheduled.
For example:
class MyClass {
private MyObject myObj;
public MyClass(MyObject myObj) {
this.myObj = myObj;
}
#Scheduled(fixedRateString = "${myObj.rate:5000}")
private void someTask() {
...
}
}
The code above, of course, does not work, I'm just giving an example of what I'm trying to do.
Any suggestions would be appreciated.
Yes you can use the #Scheduled annotation to do that with a SpEL expression (available on the #Scheduled annotation since Spring 4.3.x). Here's an example:
#Slf4j
#Configuration
#EnableScheduling
public class ExampleClass {
static class ScheduleCalculator {
public String calc() {
return "5000";
}
}
#Bean("scheduleCalculator")
public ScheduleCalculator createScheduleCalculator() {
return new ScheduleCalculator();
}
#Scheduled(fixedRateString = "#{scheduleCalculator.calc()}")
public void someTask() {
log.info("Hello world");
}
}
However, just because you can do it like this doesn't mean you necessarily should.
Your code may be easier to follow to folks that have to maintain it in the future if you use the spring task scheduler plus you get control of the thread pool used for scheduling instead of relying on the shared executor that all #Scheduled tasks get lumped into.
Unfortunately the spring bean creation process will not read local variables like that.
You can use the Spring TaskScheduler class.
Essentially you just have to define a thread pool that you will use to run the scheduled tasks (as a bean) and run taskScheduler.schedule(runnable, new CronTrigger("* * * * *")). There is a detailed example here:
https://www.baeldung.com/spring-task-scheduler
You can do like follow:
#Component
#ConfigurationProperties(prefix = "my.obj")
public class MyObject {
private String cronExecExpr = "*/5 * * * * *";
// getter and setter
}
class MyClass {
private MyObject myObj;
public MyClass(MyObject myObj) {
this.myObj = myObj;
}
#Scheduled(cron = "${my.obj.cron-exec-expr:*/5 * * * * *}")
private void someTask() {
...
}
}
As you can see her : https://www.baeldung.com/spring-scheduled-tasks
You can do that like follow :
A fixedDelay task:
#Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}")
A fixedRate task:
#Scheduled(fixedRateString = "${fixedRate.in.milliseconds}")
A cron expression based task:
#Scheduled(cron = "${cron.expression}")
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
Currently we have a single schedule that would execute the method
#Scheduled(cron = "${SEND_SCHEDULE_1}")
public void scheduledTask() {...}
We are looking for a solution that would schedule the same task on two different times during the week, one timer for Mon-Sat and another timer for Sunday.
Is there something similar to JSR-000318 - #Schedules and #Schedule
like so?
#Schedules(
{
#Schedule(hour=”11”, dayOfWeek=”Mon-Sat”),
#Schedule(hour=”10”, dayOfWeek=”Sun”)
})
public void scheduledTask() { ... }
You can use org.springframework.scheduling.annotation.Schedules
#Schedules({#Scheduled(cron=""), #Scheduled(cron="")})
What I want to be able to do is read a file with a bunch of batch files (and args) and create a quartz job for each of those entries. I know I'm missing something obvious, but I can't seem to find how to do this anywhere on google. Everything I'm finding says a new class has to be coded for each job which can't be externally constructed. I can't seem to find how to create an instance of a class which I can pass into the scheduler.
public class MyJob implements Job{
private String[] jobArgs = null;
public MyJob(String[] jobArgs){
this.jobArgs = jobArgs;
}
public void execute(JobExecutionContext arg0) throws JobExecutionException{
ExternalProcess ep - new ExternalProcess();
try{
ep.runExecutableCommand(jobargs);
}catch(Exception e){...}
}
}
public class JobScheduler {
...
List<String[]> jobArgList = loadJobListFromDisk();
List<MyJob> = new ArrayList<MyJob>();
for(String[] jobArgs : jobList){
MyJob myJob = new MyJob(jobArgs);
// Is it possible to pass in a reference to an instance somehow
// instead of letting the scheduler create the instance based on
// the class definition? I know this syntax doesn't work, but this
// is the general idea of what I'm trying to do.
JobDetail jobDetail = JobBuilder.newJob(myJob).withIdentity...
}
}
In quartz 2, you need to use a JobDataMap (stored in the jobDetail) to transfer parameters to the execute method. It will be available in the context.getJobDetail().getJobDataMap()
From what ive had quality time with Quartz Scheduler or rather JobBuilder, its written in such a dumb way it only accepts Class object as parameter. I was not able to find a way to pass Job Object into the builder.
It's a very very bad design, resulting in future problems with writing generic solutions for Quartz in version 1.X at least.