I am a bit new to Quartz. Is there a way to update the job execution interval for an already submitted Quartz job? Does this interval get updated immediately? Do you have to start the job once again after rescheduling it?
I found the following link but I don't know which libraries is the code referring to since my quartz jars don't contain some of the classes used in the link. Also, where did the triggerKey method come from? Is this some kind of a static import?
http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/cookbook/UpdateTrigger.html
I want to update the job execution interval to a very large number in one of my JUnit test cases since I don't want the job to interfere with the state of the class under test. Once the test case completes, I want to reset the the job execution interval to the actual value that will be used in production
You have to reschedule the job by creating a new trigger.
public void execute(JobExecutionContext context) throws JobExecutionException {
Trigger newTrigger = what_ever_you_want;
Trigger oldTrigger = context.getTrigger();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.rescheduleJob(oldTrigger.getKey(), newTrigger);
}
This will replace the same job with a new trigger fire time.
Perhaps there is a static method triggerKey() somewhere in the Quartz library. However, I managed to reschedule an existing Quartz job (using Quartz 2.3.2) without using this (potential) method, but rather using the TriggerKey class as follows:
boolean updateExisting = true; // try it also with false
int aveId = 1234; // change it as you wish
java.util.Date closeDate = new java.util.Date(); // change it as you wish
SchedulerFactory sf = new StdSchedulerFactory("... /quartz_priority.properties");
Scheduler scheduler = sf.getScheduler();
TriggerKey triggerKey = new TriggerKey("trigger" + aveId, "group1");
if (updateExisting) {
Trigger oldTrigger = scheduler.getTrigger(triggerKey);
TriggerBuilder oldTriggerBuilder = oldTrigger.getTriggerBuilder();
Trigger newTrigger = oldTriggerBuilder.startAt(closeDate).build();
scheduler.rescheduleJob(triggerKey, newTrigger);
} else {
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(closeDate).build();
// Define job instance
JobDetail job1 = JobBuilder.newJob(<YOUR_JOB_CLASS_NAME>.class).withIdentity("job" + aveId, "group1").build();
JobDataMap map = job1.getJobDataMap();
map.put(<PARAMETER_NAME>, aveId);
// Schedule the job with the trigger
scheduler.scheduleJob(job1, trigger);
}
Related
I need to schedule some periodic jobs and I have hit a roadblock with Quartz.
For example:
I do not think this is possible with Quartz (with a single expression/job):
Run a job every day between 8:30 and 12:45, with a period of five minutes. E.g. 8:30, 8:35, 8:40, .... 12:40, 12:45.
If it was between 8:00 and 12:00 it would be easy but I could not find a way to schedule it except handling 8:30-9:00 and 12:00-12:45 with separate expressions, which I do not want to.
Am I wrong in assuming that this is non-trivial with Quartz? I have also searched for some alternatives but most seem to have a similar cron syntax and I am not sure they could handle it either.
Is there a finer-grained scheduling library that I can use in this scenario?
This is perfectly possible with Quartz and a single trigger. People often focus on Cron triggers, but Quartz supports other trigger types and these are often more suitable. To implement your scheduling scenario, I recommend that you look into the Quartz DailyTimeIntervalTrigger.
In the screenshot below you can see a DailyTimeIntervalTrigger example with attribute values to cover your use-case.
I'm not certain you can do this, as you've hinted at. It seems possible to create a custom Trigger to do it, but then it becomes quite a bit of work. The other option is to split the dual Triggers by day, not time.
public class TestQuartz {
class ActualJob implements Job {
#Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
}
}
class DailyJob implements Job {
#Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
// Run from now (8:45) every 5 minutes until 12:45 (endDate)
Trigger trigger =
newTrigger()
.startNow()
.endAt(endDate) // 12:45 PM TODAY
.withSchedule(
cronSchedule("0 0/5 * 1/1 * ? *"))
.build();
try {
Scheduler sched = context.getScheduler();
sched.scheduleJob(newJob(ActualJob.class).build(), trigger);
} catch (SchedulerException ex) {
throw new JobExecutionException(ex);
}
}
}
#Test
public void testQuartz() throws SchedulerException {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
// Run once a day starting at 8:45 AM
Trigger dailyTrigger =
newTrigger().withSchedule(cronSchedule("0 45 8 1/1 * ? *")).build();
JobDetail job = newJob(DailyJob.class).build();
sched.scheduleJob(job, dailyTrigger);
}
}
I have the following method that builds a new JobDetail instance;
private JobDetail getJob(JobID jobID) throws SchedulerException {
Class<? extends Job> jobClass = jobID.getJobClass();
if(jobClass != null){
return JobBuilder
.newJob(jobClass)
.withIdentity(jobID.jobName(), jobID.jobGroup())
.requestRecovery(true)
.build();
}
return null;
}
Note: JobID is just a helper class of mine that provides all the necessary raw data required to build a JobDetail
Then another method that does the actual scheduling;
doSchedule(Scheduler scheduler, JobDetail job, String triggerName){
Trigger trigger = buildSomeTrigger(triggerName);
//check whether job exists in scheduler instance
if(scheduler.checkExists(job.getKey())){
//If trigger also exists, then it probably holds new info
//So, reschedule the existing job with the trigger
if(scheduler.checkExists(trigger.getKey())){
System.out.println("update job with Trigger");
job = job.getJobBuilder().storeDurably(true).build();
scheduler.addJob(job, true);
Trigger oldTrigger = scheduler.getTrigger(trigger.getKey());
scheduler.rescheduleJob(oldTrigger.getKey(), trigger);
}else{
//If trigger does not exist, it's an entirely new trigger
//Add the new trigger to the existing job
System.out.println("save new trigger for job");
trigger = trigger.getTriggerBuilder().forJob(job).build();
scheduler.scheduleJob(trigger);
}
}else{
//If job does not exist, schedule new job with the trigger
System.out.println("schedule new job with trigger");
scheduler.scheduleJob(job, trigger);
}
}
EDIT: Here's what getNewSchedulerInstance() looks like;
private Scheduler getNewSchedulerInstance() {
Scheduler scheduler = null;
try {
scheduler = new StdSchedulerFactory("quartz.properties").getScheduler();
scheduler.setJobFactory(cdiJobFactory); //<== Utilizes the JobFactory implementation specified in the CdiJobFactory.java class
scheduler.getListenerManager().addJobListener(new SimpleJobListener());
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener());
} catch (SchedulerException ex) {
System.out.println("Scheduler instantiation failed!");
ex.printStackTrace();
}
return scheduler;
}
Then finally in another method, I have the following;
//This returns a new instance of Scheduler
Scheduler scheduler = getNewSchedulerInstance();
if(scheduler != null){
JobID jobID = new ExtendedJobID(); // <== extends JobID
JobDetail job = getJob(jobID);
doSchedule(scheduler, job);
}
Normally I expect this to print;
schedule new job with trigger
But instead it's printing;
save new trigger for job
EDIT: And hence throws the following exception;
org.quartz.JobPersistenceException: Couldn't store trigger 'GOALS.ACTIVATE_GOALS_0:0' for 'GOALS.ACTIVATE_GOALS' job:
The job (GOALS.ACTIVATE_GOALS) referenced by the trigger does not exist.
[See nested exception: org.quartz.JobPersistenceException: The job (GOALS.ACTIVATE_GOALS) referenced by the trigger does not exist.]
This suggests that the scheduler.checkExists(job.getKey()) returns true
scheduler.checkExists(trigger.getKey()) returns false
Yet exception says that The job (*jobName*) referenced by the (*trigger*) does not exist
So, this issuse only gets me confused as it seems that scheduler.checkExists(...) is failing to appropriately check the existence of the job or not in the scheduler
The Exception shouldn't even get thrown if the check did what I expect it to do;
But as you can see, the JobDetail is newly built and has not in any way been previously associated with the scheduler...
So I'm wondering whether i'm missing some Quartz Scheduler points?...
If so, can someone please explain what I might be doing wrong here?...
Or could this actually be a bug?...
Are there any work-arounds?
I need to execute scheduler immediately when I will call the scheduler and next time it will execute base on the cron expression. But here my code which is only executing after 10 minute but not executing when I run this class/application.
QuartzConfigure.java for registering and calling the execute method:
public class QuartzConfigure {
public static void main(String args[]) throws Exception {
// specify the job' s details..
JobDetail job = JobBuilder.newJob(QuartzSchduleJob.class)
.withIdentity("testJob")
.build();
//this is specify using chron expression using chrone expression
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("Group2")
.withSchedule(CronScheduleBuilder.cronSchedule("0 /10 * * * ?"))
.startNow().build();
//schedule the job
SchedulerFactory schFactory = new StdSchedulerFactory();
Scheduler sch = schFactory.getScheduler();
sch.start();
sch.scheduleJob(job, trigger);
}
}
QuartzSchduleJob.java for job:
public class QuartzSchduleJob implements Job {
#Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("calling jobSchedulling::"+System.currentTimeMillis());
}
}
With your current code, your 'scheduler' starts immediatly after sch.start(), so I am guessing you want to know how force your Job to trigger when your 'scheduler' starts.
If so, you can not achieve this with only a cron expression but I have two solutions for you.
If you want your Job to be trigger at start and then every ten minutes after, consider using a SimpleScheduleBuilder. In your code, replace the CronExpressionSchedule :
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "Group2")
.withSchedule(ScheduleBuilder.simpleSchedule()
.withIntervalInMinutes(10)
.repeatForever())
.startNow()
.build();
If you want your Job to trigger at start and then fires on a scheduling based on your Cron expression. Consider using a second trigger :
[...]
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "Group2")
.withSchedule(ScheduleBuilder.simpleSchedule())
.startNow()
.build();
[...]
sch.start();
sch.scheduleJob(job, trigger);
sch.scheduleJob(job, trigger2);
JobDetail job = new JobDetail();
job.setName("dummyJ");
job.setJobClass(NotificationCreater.class);
SimpleTrigger trigger = new SimpleTrigger();
trigger.setName("mn");
trigger.setStartTime(new Date(System.currentTimeMillis() + 1000));
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(30000);
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
i am using above code to schedule my activity in NotificationCreater.class but i get error message
error:-Unable to store Job with name: 'dummyJ' and group: 'DEFAULT', because one already exists with this identification.
You can use the init method in Servlet to initialise and start of the schedule. You should also use the destroy method in Servlet to remove the scheduled job from the pool once you application is removed to avoid the same error happening during re-deployment. You can do something like scheduler.unscheduleJob() and scheduler.shutdown() to remove the job and stop the scheduler from destroy method.
If using servlets, and want to run your job on application startup, I guess this is how you should proceed to achieve.
The Job Class
public class DummyJob{
public DummyJob() throws ParseException, SchedulerException {
JobDetail job = new JobDetail();
job.setName("dummyJ");
job.setJobClass(NotificationCreater.class);
SimpleTrigger trigger = new SimpleTrigger();
trigger.setName("mn");
trigger.setStartTime(new Date(System.currentTimeMillis() + 1000));
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(30000);
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
}
The servlet
public class JobInitializerServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 5102955939315248840L;
/**
* Application logger to log info, debug, error messages.
*/
private static final Logger APP_LOGGER = Logger.getLogger("appLogger");
/**
* #see Servlet#init(ServletConfig) Initializes DummyJob
*/
public void init(ServletConfig config) throws ServletException {
try {
DummyJob scheduler = new DummyJob();
} catch (java.text.ParseException e) {
APP_LOGGER.error(e.getLocalizedMessage(), e);
} catch (SchedulerException e) {
APP_LOGGER.error(e.getLocalizedMessage(), e);
}
}
}
And servlet Mapping
<servlet>
<description>
</description>
<display-name>JobInitializerServlet</display-name>
<servlet-name>JobInitializerServlet</servlet-name>
<servlet-class>com.job.servlet.JobInitializerServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
This will initiate the job as soon as you deploy or start your application. Hope this helps.
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
The trigger is being set for the indefinite repeat counts.
Meaning, the trigger will be there in database forever.
And as a result, the job associated with the trigger would also exist in database
forever.
So, you executed your program for the first time and become glad to see it running.
You stopped the execution and had a coffee break.
You then come back and want to show this to your manager and ##$%## BOOM #$%#$%#$5.
You trying to create the job and trigger with the name which are already in
database. And scheduler will offcourse prevents you from doing this.
Solutions :
Wipe out all the data from the quartz database tables before you start the next execution of
program. OR
Don't use an indefinite trigger . Use a simple one . A one time execution or two or three but not ~. OR
Use RAMJobStore.
We have several Quartz jobs configured in our application. During development, we leave the quartz scheduler in standby - however, we sometimes want to start a job manually (for development purposes). If I call fireTrigger, it tells me I need to start the scheduler. However, if I start the scheduler, it will also immediately schedule all the other jobs, which is not what I want (since they may trigger while I'm debugging the manually fired job).
I could pause all triggers when I start the scheduler, but then I have to deal with misfire instructions etc.
Is there a simple way to fire off a job manually without having to deal with pausing and misfires (i.e. a fireTrigger which works even if the scheduler is in standby)?
this is the loop you will require to fire the job manually:
SchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = stdSchedulerFactory.getScheduler();
// loop jobs by group
for (String groupName : scheduler.getJobGroupNames()) {
// get jobkey
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
String jobName = jobKey.getName();
String jobGroup = jobKey.getGroup();
scheduler.triggerJob(jobName, jobGroup);
}
}
All the Jobs registered in the Quartz Scheduler are uniquely identified by the JobKey which is composed of a name and group . You can fire the job which has a given JobKey immediately by calling triggerJob(JobKey jobKey) of your Scheduler instance.
// Create a new Job
JobKey jobKey = JobKey.jobKey("myNewJob", "myJobGroup");
JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity(jobKey).storeDurably().build();
// Register this job to the scheduler
scheduler.addJob(job, true);
// Immediately fire the Job MyJob.class
scheduler.triggerJob(jobKey);
Note:
scheduler is the Scheduler instance used throughout your application . Its start() method should be already called after it is created.
The job is the durable job which cannot attach any triggers or cron to it .It can only be fired programmatically by calling `triggerJob(JobKey jobKey)`.
You can try to add a trigger filter in your scheduler
this.scheduler.addGlobalTriggerListener(new DebugExecutionFilter());
The debug execution filter will add a veto when the execution is not volatile (not scheduled to run immediately) and you are in debug mode .
Here is an implementation example :
private static class DebugExecutionFilter implements TriggerListener
{
public DebugExecutionFilter()
{
}
#Override
public String getName()
{
return "Task execution filter";
}
#Override
public void triggerFired(Trigger trigger, JobExecutionContext context)
{
// Do nothing
}
/* (non-Javadoc)
*
* #see org.quartz.TriggerListener#vetoJobExecution(org.quartz.Trigger, org.quartz.JobExecutionContext) */
#Override
#SuppressWarnings("unchecked")
/**
* A veto is added if :
* - For non volatile trigger if we are in debug mode
*/
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context)
{
try
{
//
if ( !trigger.isVolatile() && isDebugMode() )
{
return true;
}
//task is run by scheduler.triggerJobWithVolatileTrigger() for immediate schedule
//or task is schedule and we are not in debugMode
return false;
}
#Override
public void triggerMisfired(Trigger trigger)
{
// do nothing
}
#Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode)
{
// do nothing
}
}
No need for start-time and end-time.
<trigger>
<cron>
<name>TestTrigger</name>
<group>CronSampleTrigger</group>
<description>CronSampleTrigger</description>
<job-name>TestJob</job-name>
<job-group>jobGroup1</job-group>
<!--<start-time>1982-06-28T18:15:00.0Z</start-time>
<end-time>2020-05-04T18:13:51.0Z</end-time>-->
<cron-expression>0 0/1 * * * ?</cron-expression>
</cron>
</trigger>