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>
Related
How to check if scheduled Quartz cron job is running or not? Is there any API to do the checking?
scheduler.getCurrentlyExecutingJobs() should work in most case. But remember not to use it in Job class, for it use ExecutingJobsManager(a JobListener) to put the running job to a HashMap, which run before the job class, so use this method to check job is running will definitely return true. One simple approach is to check that fire times are different:
public static boolean isJobRunning(JobExecutionContext ctx, String jobName, String groupName)
throws SchedulerException {
List<JobExecutionContext> currentJobs = ctx.getScheduler().getCurrentlyExecutingJobs();
for (JobExecutionContext jobCtx : currentJobs) {
String thisJobName = jobCtx.getJobDetail().getKey().getName();
String thisGroupName = jobCtx.getJobDetail().getKey().getGroup();
if (jobName.equalsIgnoreCase(thisJobName) && groupName.equalsIgnoreCase(thisGroupName)
&& !jobCtx.getFireTime().equals(ctx.getFireTime())) {
return true;
}
}
return false;
}
Also notice that this method is not cluster aware. That is, it will only return Jobs currently executing in this Scheduler instance, not across the entire cluster. If you run Quartz in a cluster, it will not work properly.
If you notice in the QUARTZ_TRIGGERS table, there is a TRIGGER_STATE column. This tells you the state of the trigger (TriggerState) for a particular job. In all likelihood your app doesn't have a direct interface to this table but the quartz scheduler does and you can check the state this way:
private Boolean isJobPaused(String jobName) throws SchedulerException {
JobKey jobKey = new JobKey(jobName);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobDetail.getKey());
for (Trigger trigger : triggers) {
TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
if (TriggerState.PAUSED.equals(triggerState)) {
return true;
}
}
return false;
}
Have you looked at this answer? Try with:
scheduler.getCurrentlyExecutingJobs()
I have a started scheduler and have one job running on it. This is the main scheduler thread I shall be running.
public class MyApp {
Scheduler scheduler1;
public static void main(String[] args) {
run();
}
public static void run(){
try {
JobDetail job = JobBuilder.newJob(Job.class)
.withIdentity("JoeyJob", "group1").build();
Trigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("cronTrigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
.build();
scheduler1 = new StdSchedulerFactory().getScheduler();
scheduler1.start();
System.out.println(scheduler1.getSchedulerInstanceId());
scheduler1.scheduleJob(job,trigger1);
Thread.sleep(1000000);
scheduler1.shutdown();
}
catch(Exception e){
e.printStackTrace();
}
}
I wish to run another another job with a trigger on the very same scheduler but I need to access it from a different java executable using probably the scheduler name or any such parameter. I realize that the scheduler name returns something like 'defaultScheduler' and the Instance ID returns 'NON_CLUSTERED' I need to develop an application to run a single scheduler thread and constantly add/remove update jobs of sending emails. As this will be initialized used by a servlet. Is there a way I can access this scheduler from the memory from a different executable instance. This is what I am looking for.
public class Test {
public static void main(String[] args) throws SchedulerException {
run();
}
public static void run()throws SchedulerException{
JobDetail job = JobBuilder.newJob(Job2.class)
.withIdentity("Jake", "group2").build();
Trigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("cronTrigger2", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
.build();
Scheduler scheduler= new StdSchedulerFactory().getScheduler("scheduler-name-something");
scheduler.scheduleJob(job,trigger1);
}
}
Is there a way to use the Scheduler Instance ID and the Scheduler Name to do this?
I checked the documentation, there is no way to do what I was looking for.
I am using quartz with cron to schedule 2 jobs . One runs at 12:00 and the other at 14:00 and it runs perfectly. Here's my code.
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
JobDetail job1 = JobBuilder.newJob(FirstInvoiceGeneration.class)
.withIdentity("FirstInvoiceGenerationJob", "group1").build();
Trigger trigger1 = TriggerBuilder
.newTrigger()
.withIdentity("FirstInvoiceGenerationTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?")).build();
//Simple invoice generation to check which invoice to be generated today
JobDetail job2 = JobBuilder.newJob(TodayInvoiceGeneration.class)
.withIdentity("TodayInvoiceGenerationJob", "group1").build();
Trigger trigger2 = TriggerBuilder
.newTrigger()
.withIdentity("TodayInvoiceGenerationTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule(0 0 14 * * ?")).build();
//Schedule it
Scheduler scheduler;
try {
scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job1, trigger1);
scheduler.scheduleJob(job2, trigger2);
} catch (SchedulerException e) {
e.printStackTrace();
}
}}
My two classes for running two jobs are :
public class FirstInvoiceGeneration implements Job {
#Override
public void execute(JobExecutionContext arg0)
throws JobExecutionException {
System.out.println("Listener running.");
}
}
my second class for second job is
public class FirstInvoiceGeneration implements Job {
#Override
public void execute(JobExecutionContext arg0)
throws JobExecutionException {
System.out.println("Listener running.");
}
}
Now this runs perfectly , but what i want to do is to have 1 job that runs these two codes. Now i can use this cron expression --> "0 0 12,14 * * ?"
this will make the job run at 12pm , 2pm (my guess :p ). But i want to have only one class that execute one code when time is 12pm and other when time is 2pm. So can someone tell me how i can do it?
Reference. As you can see you can send data to a job by usingJobData() method to a job e.g. ("WORK","FIRST"),("WORK","SECOND").
In a single job class only check for the WORK key and accordingly do the operations you want to do. JobExecutionContext you can retrieve the job data using context.getJobDetails().getJobDataMap()
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.