I'm using Spring to inject a Quartz scheduler (abstracted with Spring's TaskScheduler interface) into my app that loads jobs configured from a database at startup.
It adds each job in the scheduler something like this:
TaskScheduler taskScheduler = ...;//injected
Runnable runableThing = ...;
String cronExpression = ...; //from DB
taskScheduler.schedule(runableThing, new CronTrigger(cronExpression));
my question is this: Is it possible to specify something like a job_id that can subsequently be used to cancel the job/trigger - say in response to a user selecting the job to be cancelled in the web interface?
I've looked at the Spring docs and can't see a way to do this.
Any ideas gratefully received.
Unscheduling a Particular Trigger of Job
scheduler.unscheduleJob(triggerName, triggerGroup);
Deleting a Job and Unscheduling All of Its Triggers
scheduler.deleteJob(jobName, jobGroup);
Ref: http://www.opensymphony.com/quartz/wikidocs/UnscheduleJob.html
ScheduledFuture<V> job = taskSchedule.schedule(runableThing, new CronTrigger(cronExpression))
job.cancel(true);
Related
I am configuring Quartz job with Spring boot. The requirement is to execute the job immediately without attaching any schedule.
Here is what my code looks like
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
String jobName = jobName(taskContext);
factoryBean.setJobClass(MyJobClass.class);
factoryBean.setDurability(true);
factoryBean.setApplicationContext(applicationContext);
factoryBean.setName("Hello job");
factoryBean.setGroup("Hello job group");
JobDataMap jobData = new JobDataMap(new HashMap<>());
factoryBean.setJobDataMap(jobData);
factoryBean.afterPropertiesSet();
JobDetail job = factoryBean.getObject();
Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduler.addJob(job, replace);
scheduler.triggerJob(job.getKey());
And here is how quartz.properties looks like
org.quartz.scheduler.instanceName=springBootQuartzApp
org.quartz.scheduler.instanceId=AUTO
org.quartz.threadPool.threadCount=10
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.misfireThreshold=2000
org.quartz.jobStore.tablePrefix=qrtz_
org.quartz.jobStore.isClustered=false
org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownHook.cleanShutdown=TRUE
The problem is that the job is not firing immediately and is getting picked up as misfire instruction. It is executed right exactly after the misfireThreshold.
Please let me know, if I have missed something in the configuration or didn't call any appropriate API.
I got the same issue.
If your quartz is using a data source with transaction: #EnableTransactionManagement.
Please add #Transactional to the method of your code, then the transaction is committed immediately.
Later the scheduler thread looks up the db again and fire it finally.
I want to configure scheduler in my application where I have to set cron expression with database values dynamically. When application starts, a method should fetch the database values in set them in cron expression for a particular job. Please help me with this. I am all new to quartz scheduler, spring scheduler concepts
You can very well use TaskScheduler class of Spring Scheduling in this case.
Please have a look at the class definition:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/TaskScheduler.html
scheduler.schedule(runnableTask, new CronTrigger(cron, TimeZone.getTimeZone(timezone)));
You can create a runnable task as follows:
class RunnableTask implements Runnable {
#Override
public void run() {
//
}
}
While creating a cron trigger, you can load cron expression from database.
You may want to look at this answer. https://stackoverflow.com/a/4499229/82632
Basically you need to autowire TaskScheduler class then programmatically add jobs with it.
In my web-app (Tomcat 6) I define a Quartz Scheduler in a class extending HttpServlet: this class is called to init.
The scheduler runs immediately and it has an interval of 1 minute, but after the first step it's not running.
When I change the parameter of scheduler by webpage, the scheduler is running correctly with the same code.
This is the code:
JobDetail job = newJob(ClassOfTask.class).withIdentity(NAME_JOB_MAIL, NAME_JOB_THREAD).build();
//various code
String cronExpression = buildCronExpression();
Trigger trigger = newTrigger().withIdentity(NAME_TRIGGER).startAt(startJob).endAt(endJob).forJob(job.getKey()).withSchedule(cronSchedule(cronExpression)).build();
scheduler.addJob(jobDetail, true);
scheduler.scheduleJob(trigger);
I tried to insert
scheduler.start();
but the problem remains.
When I modify the scheduled task in web page, I use this method
scheduler.rescheduleJob(oldTrigger.getKey(), trigger);
and in this case it works.
What's it the problem?
I'm looking for a lib that allow me to do
define a worker that will be invoked once on a specific time in the future (not need the re-schedule / cron like featrure) i.e. a Timer
The worker should accept a context which withe some parameters / inputs
all should be persistent in the DB (or file) the worker
worker should be managed by spring -- spring should instantiate the worker so it can be injected with dependencies
be able to create timers dynamically via API and not just statically via spring XML beans
nice to have:
support a cluster i.e. have several nodes that can host a worker. each store jobn in the DB will cause invokaction of ONE work on one of the nods
I've examined several alternatives none meets the requirements:
Quartz
when using org.springframework.scheduling.quartz.JobDetailBean makes quartz create your worker instance (and not by spring) so you can't get dependecy ijection, (which will lead me to use Service Locator which I want to avoid)
while using org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean you can't get a context. your Worker expose one public method that accepts no arguments.In addition when using MethodInvokingJobDetailFactoryBean you can't use persistence (form the Javadoc)
Note: JobDetails created via this FactoryBean are not serializable and thus not suitable for persistent job stores. You need to implement your own Quartz Job as a thin wrapper for each case where you want a persistent job to delegate to a specific service method.
Spring's Timer and simple JDK Timers does not support the persistence / cluster feature
I know I can impl thing myself using a DB and Spring (or even JDK) Timers but I prefer to use an a 3r party lib for that.
Any suggestions?
If you want to create the job details to generate triggers/job-details at runtime and still be able to use Spring DI on your beans you can refer to this blog post, it shows how to use SpringBeanJobFactory in conjunction with ObjectFactoryCreatingFactoryBean to create Quartz triggering objects at runtime with Spring injected beans.
For those interested in an alternative to Quartz, have a look at db-scheduler (https://github.com/kagkarlsson/db-scheduler). A persistent task/execution-schedule is kept in a single database table. It is guaranteed to be executed only once by a scheduler in the cluster.
Yes, see code example below.
Currently limited to a single string identifier for no format restriction. The scheduler will likely be extended in the future with better support for job-details/parameters.
The execution-time and context is persistent in the database. Binding a task-name to a worker is done when the Scheduler starts. The worker may be instantiated by Spring as long as it implements the ExecutionHandler interface.
See 3).
Yes, see code example below.
Code example:
private static void springWorkerExample(DataSource dataSource, MySpringWorker mySpringWorker) {
// instantiate and start the scheduler somewhere in your application
final Scheduler scheduler = Scheduler
.create(dataSource)
.threads(2)
.build();
scheduler.start();
// define a task and a handler that named task, MySpringWorker implements the ExecutionHandler interface
final OneTimeTask oneTimeTask = ComposableTask.onetimeTask("my-onetime-task", mySpringWorker);
// schedule a future execution for the task with a custom id (currently the only form for context supported)
scheduler.scheduleForExecution(LocalDateTime.now().plusDays(1), oneTimeTask.instance("1001"));
}
public static class MySpringWorker implements ExecutionHandler {
public MySpringWorker() {
// could be instantiated by Spring
}
#Override
public void execute(TaskInstance taskInstance, ExecutionContext executionContext) {
// called when the execution-time is reached
System.out.println("Executed task with id="+taskInstance.getId());
}
}
Your requirements 3 and 4 do not really make sense to me: how can you have the whole package (worker + work) serialized and have it wake up magically and do its work? Shouldn't something in your running system do this at the proper time? Shouldn't this be the worker in the first place?
My approach would be this: create a Timer that Spring can instantiate and inject dependencies to. This Timer would then load its work / tasks from persistent storage, schedule them for execution and execute them. Your class can be a wrapper around java.util.Timer and not deal with the scheduling stuff at all. You must implement the clustering-related logic yourself, so that only one Timer / Worker gets to execute the work / task.
I'm not really sure what is the best way to initialize Quartz to schedule a cron job.
My environment is Tomcat. I have one job that needs to be triggered every day.
Should I create a separate Servlet to initialize Quartz and schedule my job?
I'm thinking of creating a Servlet and on the init() schedule my job something like this:
SchedulerFactory sf=new StdSchedulerFactory();
Scheduler sched=sf.getScheduler();
JobDetail jd=new JobDetail("job1","group1",CronJob.class);
CronTrigger ct=new CronTrigger("cronTrigger","group2","0 0/1 * * * ?");
sched.scheduleJob(jd,ct);
sched.start();
I'm new to Quartz but I guess I always need to keep a reference to the SchedulerFactory in order for Quartz to be running, therefore having that on a Servlet will be best option?
You might want to take a look at the Cookbook section on the Quartz site.
There are two easy built-in methods for starting a Quartz Scheduler within a servlet environment, using either a <listener> or <servlet> in the app's web.xml.