Quartz not executing jobs randomly - quartz-scheduler

I'm trying to use Quartz in order to schedule jobs in a web app running on Glassfish. I'm using RAMJobStore. The problem is that sometimes, the job that was scheduled isn't being executed, even if it was scheduled in the past or the future. The amount of jobs are extremely low, a total of under 20 jobs scheduled at all times on the scheduler and a guaranteed maximum of 1 job running at the same time, so I presume the thread count is not an issue, I could set it to threadCount 1 and it would still work. The scheduler is also not being shut down before the servlet is being destroyed. So what can be the cause for some jobs not being run ?
StartupServlet
public void init()
{
try
{
scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
loadJobs();
}
catch (SchedulerException se)
{
se.printStackTrace();
}
}
#Override
public void destroy()
{
try
{
scheduler.shutdown();
}
catch (SchedulerException se)
{
se.printStackTrace();
}
}
Scheduling a job
JobDetail job = JobBuilder.newJob(ScheduledTransactionJob.class)
.withIdentity(transaction.getId())
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(transaction.getId())
.startAt(date)
.build();
try
{
scheduler.scheduleJob(job, trigger);
dateFormat = new SimpleDateFormat("dd MMM yyyy, HH:mm:ss");
String recurringTransactionTime = dateFormat.format(date);
logger.info("Scheduled job for " + recurringTransactionTime);
}
catch (SchedulerException se)
{
se.printStackTrace();
}
quartz.properties
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.skipUpdateCheck = true
org.quartz.scheduler.instanceName = AppScheduler
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 25
org.quartz.threadPool.threadPriority = 10
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

Seems to be working now. Haven't ran into any more problems. Could've been a config issue, as I have moved the config file in /src/main/resources.
Also try turning logging on in order to help with the debug:
log4j.logger.com.gargoylesoftware.htmlunit=DEBUG
We also added a JobTriggerListener to help with the logs:
private static class JobTriggerListener implements TriggerListener
{
private String name;
public JobTriggerListener(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void triggerComplete(Trigger trigger, JobExecutionContext context,
Trigger.CompletedExecutionInstruction triggerInstructionCode)
{
}
public void triggerFired(Trigger trigger, JobExecutionContext context)
{
}
public void triggerMisfired(Trigger trigger)
{
logger.warn("Trigger misfired for trigger: " + trigger.getKey());
try
{
logger.info("Available threads: " + scheduler.getCurrentlyExecutingJobs());
}
catch (SchedulerException ex)
{
logger.error("Could not get currently executing jobs.", ex);
}
}
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context)
{
return false;
}
}

Related

Reset scheduled job after completion

I have an scheduled job implemented with Spring batch. Right now when it finishes it doesn't start again because it is detected as completed, is it possible to reset its state after completion?
#Component
class JobScheduler {
#Autowired
private Job job1;
#Autowired
private JobLauncher jobLauncher;
#Scheduled(cron = "0 0/15 * * * ?")
public void launchJob1() throws Exception {
this.jobLauncher.run(this.job1, new JobParameters());
}
}
#Configuration
public class Job1Configuration{
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Bean
public Job job1() {
return this.jobBuilderFactory.get("job1")
.start(this.step1()).on(STEP1_STATUS.NOT_READY.get()).end()
.from(this.step1()).on(STEP1_STATUS.READY.get()).to(this.step2())
.next(this.step3())
.end()
.build();
}
}
I know I can set a job parameter with the time or the id, but this will launch a new execution every 15 minutes. I want to repeat the same execution until is completed without errors, and then, execute a new one.
You can't restart your job because you're setting the job status to COMPLETE by calling end() in .start(this.step1()).on(STEP1_STATUS.NOT_READY.get()).end().
You should instead either fail the job by calling .start(this.step1()).on(STEP1_STATUS.NOT_READY.get()).fail()
or stop the job by calling .start(this.step1()).on(STEP1_STATUS.NOT_READY.get()).stopAndRestart(step1())
Those options will mean the job status is either FAILED or STOPPED instead of COMPLETE which means that if you launch the job with the same JobParameters, it will restart the previous job execution.
See https://docs.spring.io/spring-batch/docs/current/reference/html/step.html#configuringForStop
To launch the job in a way that handles restarting previous instances or starting a new instance, you could look at how the SimpleJobService in spring-batch-admin does it and modify the launch method slightly for your purposes. This requires you to specify an incremental job parameter that is used to launch new instances of your job.
https://github.com/spring-attic/spring-batch-admin/blob/master/spring-batch-admin-manager/src/main/java/org/springframework/batch/admin/service/SimpleJobService.java#L250
#Override
public JobExecution launch(String jobName, JobParameters jobParameters) throws NoSuchJobException,
JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException,
JobParametersInvalidException {
JobExecution jobExecution = null;
if (jobLocator.getJobNames().contains(jobName)) {
Job job = jobLocator.getJob(jobName);
JobExecution lastJobExecution = jobRepository.getLastJobExecution(jobName, jobParameters);
boolean restart = false;
if (lastJobExecution != null) {
BatchStatus status = lastJobExecution.getStatus();
if (status.isUnsuccessful() && status != BatchStatus.ABANDONED) {
restart = true;
}
}
if (job.getJobParametersIncrementer() != null && !restart) {
jobParameters = job.getJobParametersIncrementer().getNext(jobParameters);
}
jobExecution = jobLauncher.run(job, jobParameters);
if (jobExecution.isRunning()) {
activeExecutions.add(jobExecution);
}
} else {
if (jsrJobOperator != null) {
// jobExecution = this.jobExecutionDao
// .getJobExecution(jsrJobOperator.start(jobName, jobParameters.toProperties()));
jobExecution = new JobExecution(jsrJobOperator.start(jobName, jobParameters.toProperties()));
} else {
throw new NoSuchJobException(String.format("Unable to find job %s to launch",
String.valueOf(jobName)));
}
}
return jobExecution;
}
I think the difficulty here comes from mixing scheduling with restartability. I would make each schedule execute a distinct job instance (for example by adding the run time as an identifying job parameter).
Now if a given schedule fails, it could be restarted separately until completion without affecting subsequent schedules. This can be done manually or programmtically in another scheduled method.
This is the solution I came up with after all the comments:
#Component
class JobScheduler extends JobSchedulerLauncher {
#Autowired
private Job job1;
#Scheduled(cron = "0 0/15 * * * ?")
public void launchJob1() throws Exception {
this.launch(this.job1);
}
}
public abstract class JobSchedulerLauncher {
#Autowired
private JobOperator jobOperator;
#Autowired
private JobExplorer jobExplorer;
public void launch(Job job) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException,
JobParametersInvalidException, NoSuchJobException, NoSuchJobExecutionException, JobExecutionNotRunningException, JobParametersNotFoundException, UnexpectedJobExecutionException {
// Get the last instance
final List<JobInstance> jobInstances = this.jobExplorer.findJobInstancesByJobName(job.getName(), 0, 1);
if (CollectionUtils.isNotEmpty(jobInstances)) {
// Get the last executions
final List<JobExecution> jobExecutions = this.jobExplorer.getJobExecutions(jobInstances.get(0));
if (CollectionUtils.isNotEmpty(jobExecutions)) {
final JobExecution lastJobExecution = jobExecutions.get(0);
if (lastJobExecution.isRunning()) {
this.jobOperator.stop(lastJobExecution.getId().longValue());
this.jobOperator.abandon(lastJobExecution.getId().longValue());
} else if (lastJobExecution.getExitStatus().equals(ExitStatus.FAILED) || lastJobExecution.getExitStatus().equals(ExitStatus.STOPPED)) {
this.jobOperator.restart(lastJobExecution.getId().longValue());
return;
}
}
}
this.jobOperator.startNextInstance(job.getName());
}
}
My job now uses an incrementer, based on this one https://docs.spring.io/spring-batch/docs/current/reference/html/job.html#JobParametersIncrementer:
#Bean
public Job job1() {
return this.jobBuilderFactory.get("job1")
.incrementer(new CustomJobParameterIncrementor())
.start(this.step1()).on(STEP1_STATUS.NOT_READY.get()).end()
.from(this.step1()).on(STEP1_STATUS.READY.get()).to(this.step2())
.next(this.step3())
.end()
.build();
}
In my case my scheduler won't start 2 instances of the same job at the same time, so if I detect a running job in this code it means that the server restarted leaving the job with status STARTED, that's why I stop it and abandon it.

How to handle job recovering in Quartz.Net

I use Quartz 3.0.7 in my application that use .NET Core 2.2 platform.
I use ms sql server for Quartz action tracking. Quartz tracks and stores its actions in database and it's fine.
Configuration of my StdSchedulerFactory:
["quartz.scheduler.instanceName"] = "StdScheduler",
["quartz.scheduler.instanceId"] = $"{Environment.MachineName}-{Guid.NewGuid()}",
["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
["quartz.jobStore.useProperties"] = "true",
["quartz.jobStore.dataSource"] = "default",
["quartz.jobStore.tablePrefix"] = "QRTZ_",
// if running MS SQL Server we need this
["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz",
["quartz.dataSource.default.connectionString"] = #"Server=DESKTOP-D64SJFJ\MSSQLSERVER14;Database=quartz;Trusted_Connection=True;",
["quartz.dataSource.default.provider"] = "SqlServer",
[$"{StdSchedulerFactory.PropertyObjectSerializer}.type"] = "json",
[StdSchedulerFactory.PropertySchedulerInterruptJobsOnShutdownWithWait] = "true",
I want to recover each interrupted Job. How should I organize logic of my IHostedService for supporting Job Recovering?
When I Shutdown my application during my job is running then When I start my application again interrupted job doesn't run.
My IHostedService code:
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IJobFactory _jobFactory;
private readonly IEnumerable<JobSchedule> _jobSchedules;
public QuartzHostedService(
ISchedulerFactory schedulerFactory,
IJobFactory jobFactory,
IEnumerable<JobSchedule> jobSchedules)
{
_schedulerFactory = schedulerFactory;
_jobSchedules = jobSchedules;
_jobFactory = jobFactory;
}
public IScheduler Scheduler { get; set; }
public async Task StartAsync(CancellationToken cancellationToken)
{
Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
Scheduler.JobFactory = _jobFactory;
await Scheduler.Start(cancellationToken);
foreach (var jobSchedule in _jobSchedules)
{
var job = CreateJob(jobSchedule);
var trigger = CreateTrigger(jobSchedule);
if (!await Scheduler.CheckExists(job.Key, cancellationToken))
{
// if the job doesn't already exist, we can create it, along with its trigger. this prevents us
// from creating multiple instances of the same job when running in a clustered environment
await Scheduler.ScheduleJob(job, trigger);
}
else
{
// if the job has exactly one trigger, we can just reschedule it, which allows us to update the schedule for
// that trigger.
var triggers = await Scheduler.GetTriggersOfJob(job.Key);
if (triggers.Count == 1)
{
await Scheduler.RescheduleJob(triggers.First().Key, trigger);
}
else
{
// if for some reason the job has multiple triggers, it's easiest to just delete and re-create the job,
// since we want to enforce a one-to-one relationship between jobs and triggers
await Scheduler.DeleteJob(job.Key);
await Scheduler.ScheduleJob(job, trigger);
}
}
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await Scheduler?.Shutdown(cancellationToken);
}
private static IJobDetail CreateJob(JobSchedule schedule)
{
var jobType = schedule.JobType;
return JobBuilder
.Create(jobType)
.WithIdentity(jobType.FullName)
.WithDescription(jobType.Name)
.RequestRecovery(true)
.StoreDurably()
.Build();
}
private static ITrigger CreateTrigger(JobSchedule schedule)
{
return TriggerBuilder
.Create()
.WithIdentity($"{schedule.JobType.FullName}.trigger")
.WithCronSchedule(schedule.CronExpression)
.WithDescription(schedule.CronExpression)
.Build();
}
}
My startup.cs:
private void ConfigureQuartz(IServiceCollection services)
{
services.AddHostedService<QuartzHostedService>();
services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<ISchedulerFactory>(new StdSchedulerFactory(StdSchedulerFactoryConfiguration()));
services.AddSingleton<AuthKeyExpiresJob>();
//services.AddSingleton<AuthKeyWillExpireJob>();
services.AddSingleton(new JobSchedule(
typeof(AuthKeyExpiresJob),
"0 14 11 ? * *"));
}

how to run multiple jobs in quartz scheduler in struts

I implemented multiple jobs in quartz scheduler as a plugin in struts but only first job is running by using the tutorial from http://www.mkyong.com/struts/struts-quartz-scheduler-integration-example/
public class QuartzPlugin implements PlugIn {
private JobDetail job = null;
private Trigger trigger = null;
private Scheduler scheduler = null;
private static Class<QuartzPlugin> clazz = QuartzPlugin.class;
public static final String KEY_NAME = clazz.getName();
private static Logger logger = Logger.getLogger(clazz);
#Override
public void destroy() {
try {
String METHODNAME = "destroy";
logger.debug("entering " + KEY_NAME + " " + METHODNAME);
scheduler.shutdown();
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void init(ActionServlet servlet, ModuleConfig modConfig) throws ServletException {
String METHODNAME = "init";
logger.debug("entering " + KEY_NAME + " " + METHODNAME);
job = JobBuilder.newJob(SchedulerJob.class).withIdentity("anyJobName","group1").build();
try {
trigger = TriggerBuilder.newTrigger().withIdentity("anyTriggerName", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?")).build();
scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
servlet.getServletContext().setAttribute(KEY_NAME, scheduler);
// define the job and tie it to our HelloJob class
JobDetail job2 = JobBuilder.newJob(HelloJob.class).withIdentity("job2", "group2").build();
// Trigger the job to run on the next round minute
Trigger trigger2 = TriggerBuilder.newTrigger().withIdentity("trigger2", "group2")
.withSchedule(CronScheduleBuilder.cronSchedule("15/45 * * * * ?")).build();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
scheduler.scheduleJob(job2, trigger2);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

Quartz jobs - disallow concurrent execution group-wide?

using Quartz, I'd like few jobs (say about 10) to execute as a chain - i.e. NOT concurrently.
They should be executed after an "accounting day change" event occur but since they all access the same DB, I dont want them to start all together. I want them to be executed sequentially instead (order doesnt matter).
I have an idea to put them into a group - say "account_day_change_jobs" and configure Quartz somehow to do the rest for me :-) Means - run sequentially all jobs from the group. I tried the API doc (both 1.8 and 2.1), tried google but didnt find anything.
Is it possible? Is it even reasonable? Other ideas how to achieve the behavior I want?
Thanks very much for any ideas :-)
Hans
The Trigger Listener class below should re-schedule any jobs that attempt to execute while another job that the listener has been configured for is running.
Ive only lightly tested it but for simple cases it should be suitable.
public class SequentialTriggerListener extends TriggerListenerSupport {
private JobKey activeJob;
private Scheduler activeScheduler;
private Queue<JobDetail> queuedJobs = new ConcurrentLinkedQueue<JobDetail>();
public String getName() {
return "SequentialTriggerListener";
}
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
synchronized (this) {
if (activeJob != null) {
getLog().debug("Queueing Sequential Job - " + context.getJobDetail().getKey().getName());
JobDetail jd = context.getJobDetail();
activeScheduler = context.getScheduler();
jd = JobBuilder.newJob().usingJobData(jd.getJobDataMap()).withIdentity(getName() + ":" + jd.getKey().getName(), jd.getKey().getGroup())
.ofType(jd.getJobClass()).build();
queuedJobs.add(jd);
return true;
} else {
activeJob = trigger.getJobKey();
getLog().debug("Executing Job - " + activeJob.getName());
return false;
}
}
}
public void triggerMisfired(Trigger trigger) {
triggerFinalized(trigger);
}
public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) {
triggerFinalized(trigger);
}
protected void triggerFinalized(Trigger trigger) {
synchronized (this) {
try {
if (trigger.getJobKey().equals(activeJob)) {
getLog().debug("Finalized Sequential Job - " + activeJob.getName());
activeJob = null;
JobDetail jd = queuedJobs.poll();
if (jd != null) {
getLog().debug("Triggering Sequential Job - " + jd.getKey().getName());
activeScheduler.scheduleJob(jd,TriggerBuilder.newTrigger().forJob(jd).withIdentity("trigger:" + jd.getKey().getName(), jd.getKey().getGroup())
.startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0).withIntervalInMilliseconds(1)).build());
}
} else {
// this should not occur as the trigger finalizing should be the one we are tracking.
getLog().warn("Sequential Trigger Listener execution order failer");
}
} catch (SchedulerException ex) {
getLog().warn("Sequential Trigger Listener failure", ex);
}
}
}
}
its has been a long time since I used quartz, however I would try two job listeners registered to listen to two different groups
the basic idea is to have one job fire from a group / list ("todayGroup"), the '("todayGroup") job listener detects the completion for good or bad. then kicks off the next job in the list. However, it saves the 'just finished' job back in the scheduler under the ("tomorrowGroup").
public class MyTodayGroupListener extends JobListenerSupport {
private String name;
private static String GROUP_NAME = "todayGroup";
public MyOtherJobListener(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
Scheduler sched = context.getScheduler();
// switch the job to the other group so we don't run it again today.
JobDetail current = context.getJobDetail();
JobDetail tomorrows = current.getJobBuilder().withIdentity(current.getKey().getName(), "tomorrow").build();
sched.addJob(tomorrows,true);
//see if there is anything left to run
Set<JobKey> jobKeys = sched.getJobKeys(groupEquals(GROUP_NAME ));
Iterator<JobKey> nextJob = null;
if(jobKeys != null && !jobKeys.isEmpty() ){
nextJob = jobKeys.iterator();
}
if(nextJob != null){
// Define a Trigger that will fire "now" and associate it with the first job from the list
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.forJob(nextJob =.next())
.build();
// Schedule the trigger
sched.scheduleJob(trigger);
}
}
}
likewise, you'll need two 'group triggers' that will fire the first job from their respective groups at the given time you want.
public class TriggerGroupDisallowConcurrentExecutionTriggerListener : ITriggerListener
{
private IScheduler activeScheduler;
private readonly object locker = new object();
private ConcurrentDictionary<string, JobsQueueInfo> groupsDictionary = new ConcurrentDictionary<string, JobsQueueInfo>();
public string Name => "TriggerGroupDisallowConcurrentExecutionTriggerListener";
public Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default)
{
//JobKey key = context.JobDetail.Key;
//Console.WriteLine($"{DateTime.Now}: TriggerComplete. {key.Name} - {key.Group} - {trigger.Key.Name}");
TriggerFinished(trigger, cancellationToken);
return Task.CompletedTask;
}
public Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{
//JobKey key = context.JobDetail.Key;
//Console.WriteLine($"{DateTime.Now}: TriggerFired. {key.Name} - {key.Group} - {trigger.Key.Name}");
return Task.CompletedTask;
}
public Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default)
{
//JobKey key = trigger.JobKey;
//Console.WriteLine($"{DateTime.Now}: TriggerMisfired. {key.Name} - {key.Group} - {trigger.Key.Name}");
TriggerFinished(trigger, cancellationToken);
return Task.CompletedTask;
}
public Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{
//JobKey key = context.JobDetail.Key;
//Console.WriteLine($"{DateTime.Now}: VetoJobExecution. {key.Name} - {key.Group} - {trigger.Key.Name}");
lock (locker)
{
//if (!groupsDictionary.ContainsKey(context.JobDetail.Key.Group))
//{
groupsDictionary.TryAdd(context.JobDetail.Key.Group, new JobsQueueInfo { QueuedJobs = new ConcurrentQueue<IJobDetail>(), ActiveJobKey = null });
var activeJobKey = groupsDictionary[context.JobDetail.Key.Group].ActiveJobKey;
//}
if (activeJobKey != null && activeJobKey != context.JobDetail.Key)
{
var queuedJobs = groupsDictionary[context.JobDetail.Key.Group].QueuedJobs;
if (queuedJobs.Any(jobDetail => jobDetail.Key.Name == context.JobDetail.Key.Name) == true)
{
//NOTE: Джоба уже есть в очереди, нет необходимости её добавлять повторно
return Task.FromResult(true);
}
else
{
//NOTE: Добавить джобу в очередь на выполнение, и не выполнять её сейчас, т.к. она будет выполнена как только подойдёт её очередь
activeScheduler = context.Scheduler;
var newJob = JobBuilder.Create(context.JobDetail.JobType).WithIdentity(context.JobDetail.Key).Build();
queuedJobs.Enqueue(newJob);
return Task.FromResult(true);
}
}
groupsDictionary[context.JobDetail.Key.Group].ActiveJobKey = trigger.JobKey;
return Task.FromResult(false);
}
}
protected void TriggerFinished(ITrigger trigger, CancellationToken cancellationToken = default)
{
lock (locker)
{
try
{
if (!groupsDictionary.ContainsKey(trigger.JobKey.Group))
{
return;
}
var queuedJobs = groupsDictionary[trigger.JobKey.Group].QueuedJobs;
if (queuedJobs.TryDequeue(out IJobDetail jobDetail))
{
//Console.WriteLine($"dequeue - {jobDetail.Key.Name}");
var task = activeScheduler.TriggerJob(jobDetail.Key, cancellationToken);
task.ConfigureAwait(false);
task.Wait(cancellationToken);
groupsDictionary[trigger.JobKey.Group].ActiveJobKey = jobDetail.Key;
}
else
{
groupsDictionary[trigger.JobKey.Group].ActiveJobKey = null;
}
}
catch (SchedulerException ex)
{
throw;
}
}
}
private class JobsQueueInfo
{
public ConcurrentQueue<IJobDetail> QueuedJobs { get; set; }
public JobKey ActiveJobKey { get; set; }
}
}

how to skip cron trigger firetime

I am transferring files from a directory on a remote host and a trigger fires jobs when interval arrives for this job.But i want be sure that if a job still working on a storage (downloading of files not yet finished) when trigger fire time arrives ,quartz going to skip this interval.I try to use this
c r on_trigger.MISFIRE_INSTRUCTION_DO_NOTHING but i seems it only for if there is no available thread for job.
public CronTrigger scheduleJob(RemoteJob job, String cronExpression,Date firstFireTime) throws SchedulerException, ParseException {
JobDetail jobDetail = new JobDetail(job.getDescription(), job.getName(), job.getClass());
CronTrigger crTrigger = new CronTrigger(
"cronTrigger", job.getName(), cronExpression);
scheduler.scheduleJob(jobDetail, crTrigger);
crTrigger.setStartTime(firstFireTime);
crTrigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
return crTrigger;
}
Create a TriggerListener that tracks if one of your download jobs is running, and and then return true for vetoing the execution of the other type of job.
I slightly modified the code above, it's worked.
/**
* not for cluster
*/
#Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
try {
List<JobExecutionContext> currentlyExecutingJobs = context.getScheduler().getCurrentlyExecutingJobs();
for (JobExecutionContext jobContext : currentlyExecutingJobs) {
if (jobContext.getTrigger().equals(trigger) &&
jobContext.getJobDetail().getKey().equals(trigger.getJobKey())) {
return true;
}
}
} catch (SchedulerException ex) {
return true;
}
return false;
}
I did as you say
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
try {
List<JobExecutionContext> jobs =
context.getScheduler().getCurrentlyExecutingJobs();
for (JobExecutionContext job : jobs) {
if (job.getTrigger().equals(context.getTrigger()) &&
!job.getJobInstance().equals(this)) {
_logger.info("There's another instance running,So job discarded " + context.getJobDetail().getGroup()+ ":"+context.getJobDetail().getName());
return true;
}
}
} catch (SchedulerException ex) {
return true;
}
return false;
}