I am adding a job to quartz scheduler. After that I call the attached debug print jobs function. It does not list the job. Is the function call to getCurrentlyExecutingJobs() maybe limited to return max. of 10 jobs?
public void scheduleManagementEmail(ManagementEmailConfig managementEmailConfig, Scheduler scheduler) throws SchedulerException
{
logger.debug("Scheduling Management Email " +
managementEmailConfig.getManagementEmailConfigId());
String jobKey = "SendManagementEmailJob_" +
managementEmailConfig.getManagementEmailConfigId();
Class<? extends Job> jobClass = SendManagementEmailJob.class;
JobDetail job = JobBuilder.newJob(jobClass).withIdentity(new JobKey(jobKey)).build();
Trigger trigger = sendManagementEmailJob.getTriggerWithSchedule(managementEmailConfig);
trigger.getJobDataMap().put("managementEmailConfigId", managementEmailConfig.getManagementEmailConfigId());
if (!scheduler.checkExists(new JobKey(jobKey)))
{
scheduler.scheduleJob(job, trigger);
}
debugPrintJobs();
}
public void debugPrintJobs() {
try {
logger.debug("Quartz Jobs");
Scheduler s_scheduler = this.getJobScheduler();
List<JobExecutionContext> currentJobs = s_scheduler.getCurrentlyExecutingJobs();
for (JobExecutionContext jobCtx : currentJobs) {
JobKey jobKey = jobCtx.getJobDetail().getKey();
JobDetail jobDetail = s_scheduler.getJobDetail(jobKey);
List<? extends Trigger> triggers = s_scheduler.getTriggersOfJob(jobKey);
Date nextFireTime = null;
if (triggers.size() > 0)
{
nextFireTime = triggers.get(0).getNextFireTime();
}
logger.debug("Name= "+ jobKey.getName() + " Group=" + jobKey.getGroup() + " NextFireTime=" + nextFireTime);
}
} catch (Exception e) {
logger.debug("debugPrintJobs:" + e.getMessage());
}
The method getCurrentlyExecutingJobs() will return only the jobs that are running, not every scheduled job.
To get every scheduled job, you should do something like this:
Scheduler scheduler = getScheduler();
try {
// All scheduled jobs
for (String groupName : scheduler.getJobGroupNames()) {
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
final List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
// Do something with the info you just got
// ...
}
}
} catch (SchedulerException e) {
log.error("Retrieving jobs", e);
}
Related
I have next JobSchedulerService:
#Slf4j
#Service
#RequiredArgsConstructor
public class QuartzServiceImpl implements JobSchedulerService {
private final SchedulerFactoryBean schedulerFactoryBean;
#Override
public void scheduleJob(LocalDateTime date, Class jobClass, boolean repeatUntilManuallyStopped, Map<String, Object> jobDataMap) {
String expirationDate = date.toString();
String name = jobClass.getName() + "_";;
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(name + expirationDate)
.storeDurably().build();
if(jobDataMap != null){
jobDetail.getJobDataMap().putAll(jobDataMap);
}
jobDetail.getJobDataMap().put("expirationDate", expirationDate);
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name + expirationDate)
.startAt(Date.from(zonedDateTime.toInstant()))
.endAt(Date.from(zonedDateTime.plusMinutes(2).toInstant()))
.withSchedule(repeatUntilManuallyStopped ?
SimpleScheduleBuilder.repeatMinutelyForever().withMisfireHandlingInstructionFireNow() : SimpleScheduleBuilder.simpleSchedule().withMisfireHandlingInstructionFireNow())
.build();
schedule(jobDetail, trigger, false);
}
#Override
public void scheduleJob(LocalDateTime date, int intervalInMinutes, int count, Class jobClass, Map<String, Object> jobDataMap) {
String expirationDate = date.toString();
String name = jobClass.getName() + "_";
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(name + expirationDate)
.storeDurably().build();
jobDetail.getJobDataMap().putAll(jobDataMap);
jobDetail.getJobDataMap().put("expirationDate", expirationDate);
LocalDateTime currentTime = LocalDateTime.now();
LocalDateTime startDateTime = date;
int decrement = count;
while (decrement != 0) {
startDateTime = startDateTime.minusMinutes(intervalInMinutes);
decrement --;
if(currentTime.compareTo(startDateTime) >= 0) {
startDateTime = currentTime;
break;
}
}
SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name + expirationDate)
.startAt(Date.from(startDateTime.atZone(ZoneId.systemDefault()).toInstant()))
.withSchedule(SimpleScheduleBuilder.repeatMinutelyForTotalCount(count - decrement, intervalInMinutes)
.withMisfireHandlingInstructionFireNow())
.build();
schedule(jobDetail, trigger, false);
}
public Date schedule(JobDetail jobDetail, SimpleTrigger trigger, boolean isUnique) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
try {
boolean alreadyExists = scheduler.checkExists(jobDetail.getKey());
if(isUnique && alreadyExists) {
throw new ApiException(ApiError.RESOURCE_EXISTS);
} else if(!alreadyExists){
Date date = scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
return date;
} else {
return trigger.getStartTime();
}
} catch (ObjectAlreadyExistsException e) {
if(isUnique) {
ExceptionTracker.captureException(e);
log.error("Could not schedule job with key {} and description {}. Job already exists", jobDetail.getKey().getName(), jobDetail.getDescription());
throw new ApiException(ApiError.INTERNAL_SERVER_ERROR);
} else {
return trigger.getStartTime();
}
} catch (Exception e){
ExceptionTracker.captureException(e);
log.error("Could not schedule job with key {} and description {}. Localized message => {}", jobDetail.getKey().getName(), jobDetail.getDescription(), e.getLocalizedMessage());
throw new ApiException(ApiError.INTERNAL_SERVER_ERROR);
}
}
}
Here I am setting time for expiration after 2 minutes
.endAt(Date.from(zonedDateTime.plusMinutes(2).toInstant()))
And I shutdown the application right after it creates job and trigger. But when I startup again it runs expired trigger, even if the time has passed in table qrtz_triggers.end_time.
My properties file for Quartz:
org.quartz.scheduler.instanceName=PostcreditService
org.quartz.scheduler.instanceId=AUTO
org.quartz.threadPool.threadCount=5
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=60000
org.quartz.jobStore.tablePrefix=postcredit.qrtz_
org.quartz.jobStore.isClustered=true
org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownHook.cleanShutdown=TRUE
Configs:
#Configuration
public class QuartzConfig {
#Autowired
private ApplicationContext applicationContext;
#Autowired
private DataSource dataSource;
#Bean
public JobFactory jobFactory() {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setQuartzProperties(quartzProperties());
schedulerFactory.setWaitForJobsToCompleteOnShutdown(true);
schedulerFactory.setAutoStartup(true);
schedulerFactory.setJobFactory(jobFactory());
schedulerFactory.setDataSource(dataSource);
return schedulerFactory;
}
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
}
Please, help!
You are setting trigger's expire time correctly using TriggerBuilder.endAt()
And I shutdown the application right after it creates job and trigger.
But when I startup again it runs expired trigger, even if the time has
passed in table qrtz_triggers.end_time.
This happens because on restart quartz checks for misfired triggers and try to rerun those based on the configured mirefire instruction which in your case is withMisfireHandlingInstructionFireNow. Now the thing is that this particluar instruction does not consider the trigger's endAt() time and always set a new nextFireTime and rerun it.
For your case, there are other misfire instructions that you may want to consider like withMisfireHandlingInstructionNowWithExistingCount
When the user calls the api /bet/{id}/start, the Schedule will immediately be created with name = id, group = Exchanges, every 3rd second on the minute it will automatically call the excute function to process, for example here I will call the findById(id) function of the repository to retrieve and process the data, but the result I get is java.lang.NullPointerException: null
BetController.java
#RestController
#RequestMapping("/api")
#Transactional
public class BetController extends AbstractController {
private MainScheduler mainScheduler;
#RequestMapping(value = "/bet/{id}/start", method = RequestMethod.POST)
public String addAndStartScheduleWithBetId(#PathVariable("id") Long id) {
mainScheduler.addAndStartScheduler(""+id);
return "";
}
}
MainScheduler.java
#Service
public class MainScheduler {
private Scheduler scheduler;
public MainScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}
public Scheduler addAndStartScheduler(String betId) {
try {
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(betId,"Exchanges").withSchedule(CronScheduleBuilder.cronSchedule("3 * * ? * * *")).build();
JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(betId, "Exchanges") .build();
scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println(jobDetail.getKey() + ","+ trigger.getKey() + " will run at: " + new Date());
} catch(Exception ex) {
System.out.println(ex);
}
return scheduler;
}
public boolean deleteJobDetail(String name) {
boolean flag = false;
try {
flag = scheduler.deleteJob(jobKey(name, "Exchanges"));
} catch (SchedulerException e) {
e.printStackTrace();
}
return flag;
}
}
BetRepository.java
public interface BetRepository extends CrudRepository<Bet, Long> {
Page<Bet> findAll(Pageable pageable);
Optional<Bet> findById(Long id) ;
}
ScheduleJob.java
public class ScheduleJob implements Job{
#Autowired
private BetRepository betRepository;
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println("key+group trigger: " + context.getTrigger().getKey());
Long id = Long.parseLong(context.getJobDetail().getKey().getName());
System.out.println("Bet repositorys: " + betRepository.findById(id));
}
}
13-07-2021 03:33:03.015 [35m[DefaultQuartzScheduler_Worker-3][0;39m
[1;31mERROR[0;39m
org.quartz.core.ErrorLogger.schedulerError - Job (Exchanges.5 threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: java.lang.NullPointerException: null
at com.bot.auto.utils.ScheduleJob.execute(ScheduleJob.java:21)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
... 1 common frames omitted
Modify your MainScheduler so that you get the autowired BetRepository and put it in the scheduler context before starting the schedular and get it in the Job.
#Service
public class MainScheduler {
private Scheduler scheduler;
#Autowired
private BetRepository betRepository;
public MainScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}
public Scheduler addAndStartScheduler(String betId) {
try {
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(betId,"Exchanges").withSchedule(CronScheduleBuilder.cronSchedule("3 * * ? * * *")).build();
JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(betId, "Exchanges") .build();
scheduler = StdSchedulerFactory.getDefaultScheduler();
schduler.getContext().put("betRepo",betRepository);
scheduler.start();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println(jobDetail.getKey() + ","+ trigger.getKey() + " will run at: " + new Date());
} catch(Exception ex) {
System.out.println(ex);
}
return scheduler;
}
< -----other methods as is---->
}
Change your Job class as below
public class ScheduleJob implements Job{
private BetRepository betRepository;
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
betRepository= context.get("betRepo"); or context.getScheduler().getContext().get("betRepo");
System.out.println("key+group trigger: " + context.getTrigger().getKey());
Long id = Long.parseLong(context.getJobDetail().getKey().getName());
System.out.println("Bet repositorys: " + betRepository.findById(id));
}
}
I wrote this method to remove a job from Quartz JDBC
public boolean removeJob(String jobName) {
try {
JobKey jobKey = JobKey.jobKey(jobName);
try {
Scheduler sched = schedulerFactoryBean.getScheduler();
logger.info("RESULT: " + sched.deleteJob(jobKey));
} catch (Exception e) {
throw new RuntimeException(e);
}
return true;
} catch (Exception ex) {
logger.error(ex.getMessage());
return false;
}
}
deleteJob is always returning false. So the job is not getting removed from the JDBC tables in mysql. What am I doing wrong. I only want to completely remove this job from the scheduler
Have you defined a job group during job creation? Then you may need to call jobKey(jobName, group). You can also check if job exists with scheduler.checkExists(jobKey) method which will be good for debugging.
JobKey jobKey = jobKey(jobName, group);
if (scheduler.checkExists(jobKey)) {
logger.info("job found with key: {}", jobKey);
scheduler.deleteJob(jobKey);
}
I'm using Quartz 1.8 to write some code that runs a job on a daily/weekly/monthly basis. When that job runs it should create a nested job. For some reason though, neither the nested JobDetail nor Trigger wind up with entries in the database, nor do they run.
Here's the example I'm working with:
public class QuartzSSCCE {
private static Logger LOG = Logger.getLogger(QuartzSSCCE.class);
private static long counter = 0L;
private static Scheduler scheduler;
public void scheduleOuterJob(){
try{
LOG.info("About to try to schedule OuterJob");
CronTrigger ct = new CronTrigger("OuterJobTrigger" + counter++, "OuterJobGroup", "30 * * ? * *");
ct.setStartTime((new DateTime(new Date())).plusSeconds(3).toDate());
DateTime endTime = new DateTime(new Date());
endTime = endTime.plusMinutes(5);
ct.setEndTime(endTime.toDate());
JobDetail jd = new JobDetail("OuterJobDetail" + counter++, "OuterJobGroup", OuterJob.class);
scheduler.scheduleJob(jd, ct);
} catch (Exception e){
LOG.fatal("something went wrong while scheduling the outer job", e);
}
}
public static class OuterJob implements Job {
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try{
LOG.info("running OuterJob");
//Trigger trigger = TriggerUtils.makeImmediateTrigger("InnerTriggerName" + counter++, 1, 1);
SimpleTrigger simpleTrigger = new SimpleTrigger(
"InnerTriggerName" + counter++,
"InnerTriggerGroup",
(new DateTime(new Date())).plusSeconds(10).toDate(),
(new DateTime(new Date()).plusSeconds(50)).toDate(),
1,
1000
);
JobDetail jd = new JobDetail("InnerJobDetail" + counter++, "InnerJobGroup", InnerJob.class);
scheduler.scheduleJob(jd, simpleTrigger);
} catch (Exception e){
LOG.fatal("something went wrong while scheduling the inner job", e);
}
}
}
public static class InnerJob implements Job {
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
LOG.info("InnerJob is running. The time at the beep is: " + new Date());
}
}
public void setScheduler(Scheduler scheduler) {
QuartzSSCCE.scheduler = scheduler;
}
}
This class obtains an Scheduler instance from a SchedulerFactoryBean. Elsewhere in my code I instantiate an instance of QuartzSSCCE like so:
QuartzSSCCE quartzSSCCE = new QuartzSSCCE();
quartzSSCCE.scheduleOuterJob();
I wind up seeing entries in the log for "About to try to schedule OuterJob" and "running OuterJob" but it appears that the InnerJob never gets run. I've attempted this with both a trigger returned from the call to TriggerUtils.makeImmediateTrigger() as well as a SimpleTrigger.
Why won't my InnerJob run?
First of all, my English is poor!
Quartz use reflection to create job instance, as your code show:
JobDetail jd = new JobDetail("InnerJobDetail" + counter++, "InnerJobGroup", InnerJob.class);
But nested class can't create instance directly by reflection, so the InnerJob is not fired.
I'd like to use quartz for a project, but it seems to be missing some functionality. What I need is very simple, to fire another job after one finishes execution. This ideally would be done with a stored trigger, but quartz only considers time-based triggers, and no other kind. I'm looking for some kind of a job dependency system for quartz.
In reality there should be lots of triggers, such as other jobs finishing, files being modified, database tables being updated, etc.
Here's a job that waits for 5 seconds:
public class JobA implements Job, JobListener {
public static final String LISTENER_NAME = "JobA";
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("Job A, Waiting for 5 seconds");
try {
Thread.sleep(5L * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done waiting");
}
public String getName() {
return LISTENER_NAME;
}
public void jobExecutionVetoed(JobExecutionContext context) {
//TODO
}
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
System.out.println("Job : " + jobName + " is going to start...");
}
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
// System.out.println("jobWasExecuted");
String jobName = context.getJobDetail().getKey().toString();
System.out.println("Job : " + jobName + " is finished...");
if (!jobException.getMessage().equals("")) {
System.out.println("Exception thrown by: " + jobName
+ " Exception: " + jobException.getMessage());
}
JobKey jobKey = new JobKey("JobA", "group1");
try {
context.getScheduler().triggerJob(jobKey);
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The context.getScheduler().triggerJob(jobKey) line simply doesn't work. I've tried adding full jobs, recursive jobs, and can't seem to get anything to work. How can I get another job to fire while using this listener?