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.
Related
I've got a job that needs to be run every 1 minute. I've decided to move from Spring's #Scheduled annotation to Quartz jobs to take advantage of its clustered mode during blue/green deployment. For that I used a configuration similar to this:
#Bean
public JobDetailFactoryBean fooJob() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(FooJob.class);
factoryBean.setName(JOB_IDENTITY);
factoryBean.setGroup(FooJob.class.getName());
factoryBean.setDurability(true);
return factoryBean;
}
#Bean
public SimpleTriggerFactoryBean fooTrigger(#Qualifier("fooJob") JobDetail jobDetail) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setName(JOB_IDENTITY);
factoryBean.setGroup(FooJob.class.getName());
factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(0L);
factoryBean.setRepeatInterval(INTERVAL_SECONDS * 1000);
factoryBean.setMisfireInstruction(Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY);
return factoryBean;
}
Also I've got a postgres job store configured. Nothing wrong here - it works as expected.
Now my question is: what if in some time in the future I don't need this job run anymore? Then I remove this configuration but the job and the trigger are still persisted in the job store and the job is run even though the configuration is removed.
My expectation is that when I deploy a code with no beans from above, then the job is removed from the store. Is it somehow possible?
I'm using Spring Batch with Spring cloud tasks. I have the following configuration in my job:
#Bean
public Job jobDemo(
#Value("${jobname}")String jobName,
JobBuilderFactory jobBuilderFactory,
JobCompletionNotificationListener listener
) {
return jobBuilderFactory.get(jobName)
.incrementer(new RunIdIncrementer())
.preventRestart()
.listener(listener)
.flow(stepA())
.end()
.build();
}
I don't want the restart functionality in the job, that's why I have put .preventRestart(). I want to launch a new job every time the task runs, that is, a new instance of the job to run even when the last time the job has failed or stopped or anything. But I'm getting the following error:
org.springframework.batch.core.repository.JobRestartException: JobInstance already exists and is not restartable
This happens only in the scenarios when the job does not finish sucessfully. Any ideas about the solution?
A JobInstance can only be completed once successfully. When you are starting a Spring Batch job via Spring Boot, Spring Batch handles the logic to increment a JobParameter if there is a JobParametersIncrementer provides (as you have). However...when Spring Batch does that incrementing, it only increments if the previous job was successful. In your case, you want it to always increment. Because of that, you're going to need to write your own CommandLineRunner that always increments the JobParameters.
Spring Boot's JobLauncherCommandLineRunner is where the code to launch a job exists. You'll probably want to extend that and override it's execute method to be sure job parameters are always incremented.
I create Quartz job and start scheduler
JobDetail job = newJob(InfoCrawlerJob.class)
.withIdentity("job id", "group")
.usingJobData(jobData)
.build()
SimpleTrigger trigger = newTrigger()
.withIdentity("trigger id", "trigger-group")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(100)
.withRepeatCount(10))
.build()
scheduler.scheduleJob(job, trigger)
scheduler.start()
Quartz jobs are working correclty. The main problem is Spring doesn't wait jobs to be finished. How can I fix it?
Have a look at Spring Quartz Support
Spring Reference Chapter 25.6 Using the OpenSymphony Quartz Scheduler
Class org.springframework.scheduling.quartz.SchedulerFactoryBean
This class has a method: setWaitForJobsToCompleteOnShutdown(boolean) , I would expect that this is what you need.
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 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);