How to make StatefulJobs run consecutively (not concurrently)? - java

I have a scheduler:
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory("quartz.properties");
sched = schedFact.getScheduler();
sched.start();
JobDetail jobDetail;
CronTrigger trigger;
for (ReportDetails report : reports) {
jobDetail = new JobDetail(report.getName() + _REPORT, GRP, ReportJob.class);
jobDetail.getJobDataMap().put(ReportJob.DATA_REPORT, report);
sched.addJob(jobDetail, true);
if (report.getCronExp() != null && report.getCronExp().length() > 0) {
trigger = new CronTrigger(report.getName() + _TRIGGER, GRP);
trigger.setCronExpression(report.getCronExp());
trigger.setJobGroup(GRP);
trigger.setJobName(report.getName() + _REPORT);
sched.scheduleJob(trigger);
}
}
And there is my job instance:
public class ReportJob implements StatefulJob {
private static final Logger logger = Logger.getLogger(ReportJob.class);
public void execute(JobExecutionContext context) throws JobExecutionException {
ReportDetails report = (ReportDetails) context.getJobDetail().getJobDataMap().get(DATA_REPORT);
report.getLogger().info("job for report started");
...
report.getLogger().info("Job for report ended");
}
}
The thing is that though ReportJob implements StatefulJob indeed jobs run concurrently.
QuartzScheduler_Worker-1 | job for report started
QuartzScheduler_Worker-2 | job for report started
QuartzScheduler_Worker-2 | job for report ended
QuartzScheduler_Worker-1 | job for report ended
I want them to run consecutively, one by one. How to resolve this issue?

I understood my error: StatefulJob prevents multiple instances of a job WITH THE SAME KEY from running at the same time. While I'd created the jobs with different keys.

Related

How to check which Quartz Scheduler job is not running

I have below code which i have used to check which quartz scheduler job is running. And it also send me an email the list of running scheduler job. But i dont know its not returning all the scheduler jobs which are running.And now i want to know only those scheduler job which has issues,stopped and not running. I found critical issue in my Production environment where i found that some of the scheduler jobs are not running but i really dont know which scheduler job it is.
public String getPrintJobs() {
StringBuilder sb = new StringBuilder();
try {
sb.append("Quartz Jobs\r\n\r\n");
Scheduler scheduler = this.getJobScheduler();
// 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> triggers = scheduler.getTriggersOfJob(jobKey);
Date nextFireTime = null;
int priority = 5;
if (triggers.size() > 0)
{
nextFireTime = triggers.get(0).getNextFireTime();
priority = triggers.get(0).getPriority();
}
sb.append("Name= "+ jobKey.getName() + " Group=" + jobKey.getGroup() + " NextFireTime=" + nextFireTime + " Priority=" + priority + " Paused=" + (isJobPaused(jobKey.getName())?"IS PAUSED":"NOT PAUSED") + " Triggers #=" + triggers.size() + "\r\n\r\n");
}
}
sb.append("End Quartz Jobs\r\n\r\n");
} catch (Exception e) {
logger.debug("debugPrintJobs:" + e.getMessage());
}
return sb.toString();
}
private Boolean isJobPaused(String jobName) throws SchedulerException {
Scheduler scheduler = this.getJobScheduler();
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;
}
I haven't used it for getting any problem but the following interfaces could be helped you.
TriggerListener could check misfired when Quartz couldn't start the job.
JobListener could check completed the job which is both successful and failure cases.
https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/tutorial-lesson-07.html

How to stop or pause a scheduled job in Liferay 6.1

I have a portlet that launches a scheduled job. But when I try to pause or stop it, the job continues executing. I don't know if I'm doing something wrong when creating the scheduled job or when I'm trying to stop/pause it.
Here is how I launch the job:
CronTrigger trigger = new CronTrigger("job1", "group1", "0 0/1 * 1/1 * ? *");
SimpleJob job = new SimpleJob();
MessageBusUtil.registerMessageListener(DestinationNames.SCHEDULER_DISPATCH, job);
Message message = new Message();
message.put(SchedulerEngine.MESSAGE_LISTENER_CLASS_NAME, SimpleJob.class.getName());
SchedulerEngineUtil.schedule(trigger, StorageType.PERSISTED,
"Scheduled Job", DestinationNames.SCHEDULER_DISPATCH, message, 0);
Here is the job:
public class SimpleJob implements MessageListener {
private static Log log = LogFactoryUtil.getLog(SimpleJob.class);
#Override
public void receive(Message message) throws MessageListenerException {
log.debug(" ... SimpleJob executed ... ");
}
}
And here is the function that tries to stop:
public void stopCron(ActionRequest request, ActionResponse response)throws Exception{
SimpleJob job = new SimpleJob();
MessageBusUtil.unregisterMessageListener(DestinationNames.SCHEDULER_DISPATCH, job);
SchedulerEngineUtil.pause("job1", "group1", StorageType.PERSISTED);
SchedulerEngineUtil.delete("job1", "group1", StorageType.PERSISTED);
SchedulerEngineUtil.unschedule("job1", "group1", StorageType.PERSISTED);
}
How can I stop the job?
first of all thanks for your answer! I tried it with your code, withour registering the message listener and the job doesn't fire. But with the MessageBusUtil.registerMessageListener the job is fired but with the
SchedulerEngineUtil.pause("work1", "grupo1", StorageType.PERSISTED);
The job doesn't stop.
Code of launch:
String cron = "0 0/1 * 1/1 * ? *";
CronTrigger trigger = null;
trigger = new CronTrigger("work1", "grupo1", cron);
SimpleJob job = new SimpleJob();
Message message = new Message();
message.put(SchedulerEngine.MESSAGE_LISTENER_CLASS_NAME, SimpleJob.class);
message.put(SchedulerEngine.PORTLET_ID, td.getPortletDisplay().getId());
message.put(SchedulerEngine.DESTINATION_NAME, DestinationNames.SCHEDULER_DISPATCH);
MessageBusUtil.registerMessageListener(DestinationNames.SCHEDULER_DISPATCH, job);
SchedulerEngineUtil.schedule(trigger, StorageType.PERSISTED, "Scheduled Job", DestinationNames.SCHEDULER_DISPATCH, message, 0);
And the stop in other function:
SchedulerEngineUtil.delete("work1", "grupo1", StorageType.PERSISTED);
Regards.
First: you are unregistering the wrong instance of your job:
SimpleJob job = new SimpleJob();
MessageBusUtil.unregisterMessageListener(DestinationNames.SCHEDULER_DISPATCH, job);
The given job is not the instance that you have registered in
MessageBusUtil.registerMessageListener(DestinationNames.SCHEDULER_DISPATCH, job);
As result the unregisterMessageListener will do nothing.
Second: you don't need to register any message listener at all. There is already one registered for you in SchedulerEngineHelperUtil.schedule(), using the MESSAGE_LISTENER_CLASS_NAME property from the message. But to use it correctly you will have to define the id of your portlet or web context as well:
String simpleJobClass = SimpleJob.class.getName();
message.put(SchedulerEngine.MESSAGE_LISTENER_CLASS_NAME, simpleJobClass);
message.put(SchedulerEngine.PORTLET_ID, "my-web-context or my-portlet-id");
message.put(SchedulerEngine.DESTINATION_NAME, DestinationNames.SCHEDULER_DISPATCH);
Now it should be possible to pause / cancel your job as given in your example:
SchedulerEngineUtil.delete("job1", "group1", StorageType.PERSISTED);
By the way: MessageBusUtil.registerMessageListener(DestinationNames.SCHEDULER_DISPATCH, job) will register your job for all scheduled triggers, as all of them use SCHEDULER_DISPATCH as destination name.

Quartz #DisallowConcurrentExecution not working as expected

I am using java quartz schedular. I am able to schedule jobs perfectly, though what i want is wait for job to finish before runing the second round because the time it takes to run each job varies.
I used #DisallowConcurrentExecution, what it did is only make the job to run once and never again. From job listener shows that the job finished successfully once.
Job
=============================================================
#DisallowConcurrentExecution
public class SalesJob implements Job{
List<Transaction> unsentTransaction = new ArrayList<Transaction>();
List<Sale> sales = new ArrayList<Sale>();
public void execute(JobExecutionContext jec) throws JobExecutionException {
System.out.println("Sales Job. . .");
}
}
Job Listener:
public class SalesJobListener implements JobListener{
public static final String LISTENER_NAME = "dummyJobListenerName";
public String getName() {
return LISTENER_NAME;
}
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
System.out.println("jobToBeExecuted");
System.out.println("Job : " + jobName + " is going to start...");
}
public void jobExecutionVetoed(JobExecutionContext jec) {
System.out.println("jobExecutionVetoed");
}
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("jobWasExecuted");
String jobName = context.getJobDetail().getKey().toString();
System.out.println("Job : " + jobName + " is finished...");
System.out.println("=====================================");
System.out.println("==========" + new Date() + "===========");
if (!jobException.getMessage().equals("")) {
System.out.println(
"Exception thrown by: " + jobName + " Exception: " + jobException.getMessage());
}
}
}
This is the schedular
JobKey salesJobKey = new JobKey("salesJob", "group1");
JobDetail salesJob = JobBuilder.newJob(SalesJob.class)
.withIdentity(salesJobKey).build();
Trigger salesTrigger = TriggerBuilder
.newTrigger()
.withIdentity("salesTrigger", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getListenerManager().addJobListener(
new SalesJobListener(), KeyMatcher.keyEquals(salesJobKey)
);
scheduler.start();
scheduler.scheduleJob(salesJob, salesTrigger);
PROBLEM
it executed this time, Wed Nov 25 12:01:15 EAT 2015 and now is Wed Nov 25 12:32 2015, so basically i have waited > 30 mins. . . and there is no another job
That is saying the Scheduler is not working.
WHY?
you cannot execute a job at second 15 of a minute because the pattern: 0/5 * * * * ? makes scheduler to run ONLY at seconds 0 and 5 of each minute.
Using #DisallowConcurrentExecution will prevent execution a Job if another one of same type is already running.
SOLUTION:
The mistake is in the order of your code, you execute then scheduler (scheduler.start();) before tell that it must schedule a job (scheduler.scheduleJob(salesJob, salesTrigger);):
scheduler.start();
scheduler.scheduleJob(salesJob, salesTrigger);
Check this example and swap your lines:
scheduler.scheduleJob(salesJob, salesTrigger);
scheduler.start();
That's all...
Just Add the below null check
if(null != jobException) {
if (!jobException.getMessage().equals("")) {
logger.debug("Exception thrown by: " + jobName
+ " Exception: " + jobException.getMessage());
}
}
Your code will work

How to check whether Quartz cron job is running?

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()

Spring batch restrict single instance of job only

I have one spring batch job which can be kicked of by rest URL. I want to make sure only one job instance is allowed to run. and if another instance already running then don't start another. even if the parameters are different.
I searched and found nothing out of box solution. thinking of extending SimpleJobLauncher. to check if any instance of the job running or not.
You could try to intercept the job execution, implementing the JobExecutionListener interface:
public class MyJobExecutionListener extends JobExecutionListener {
//active JobExecution, used as a lock.
private JobExecution _active;
public void beforeJob(JobExecution jobExecution) {
//create a lock
synchronized(jobExecution) {
if(_active!=null && _active.isRunning()) {
jobExecution.stop();
} else {
_active=jobExecution;
}
}
}
public void afterJob(JobExecution jobExecution) {
//release the lock
synchronized(jobExecution) {
if(jobExecution==_active) {
_active=null;
}
}
}
}
And then, inject to the Job definition:
<job id="myJobConfig">
<listeners>
<listener ref="myListener"/>
</listeners>
</job>
I solved this by creating an JobExecutionListner and with the help of JobExplorer I checked if any other instance is running if running then stop current job.I created listener so that it can be plugged in to any job that requires this kind of scenario.
Set<JobExecution> jobExecutions = ((SimpleJobExplorer) jobExplorer.getObject()).findRunningJobExecutions(jobExecution.getJobInstance().getJobName());
if(jobExecutions.size()>1){
Long currentTime = (new Date()).getTime();
for(JobExecution execution : jobExecutions ){
if(execution.getJobInstance().getId().compareTo(jobExecution.getJobInstance().getId())!=0 && (currentTime - execution.getStartTime().getTime()) <lockOverideTime){
jobExecution.stop();
throw new IllegalStateException("Another instance of the job running job name : " +jobExecution.getJobInstance().getJobName() );
}
}
}
Or, in response to REST URL, check using JobExplorer if your job is running using job's specifics business rules
I think a simple method like the following might do the trick:
#Autowire
private JobExplorer jobExplorer;
private boolean isJobRunning(Job job) {
Set<JobExecution> jobExecutions = jobExplorer.findRunningJobExecutions(job.getName());
return !jobExecutions.isEmpty();
}
Then, prior to executing your job make the check:
private void executeJob(Job job, #Nonnull JobParameters params) {
if (isJobRunning(job)) {
return;
}
try {
jobLauncher.run(job, params);
} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) {
log.error("could not run job " + jobIdentifier, e);
}
}

Categories

Resources