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
Related
The task of job is simple, just output the time in console. I need a button for excuting the logic immediately.
XxxService.class
#Override
#Transactional(rollbackFor = Exception.class)
public void run(Long[] jobIds) {
for(Long jobId : jobIds){
ScheduleUtils. run(scheduler, this.getById(jobId));
}
}
ScheduleUtils.class
public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
try {
JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);
System.out.println(Thread.currentThread()+"need hello..."+new Date());
scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
} catch (SchedulerException e) {
throw new RRException("run fail", e);
}
}
MyJob:
#Data
#Component("HelloJob")
public class HelloJob extends CustomJob {
public String jobDescription = "演示Job";
#Override
public void run(String params) {
System.out.println(
Thread.currentThread() +"hello..."+new Date() + "params: " + params);
}
}
When I add #Transactional on the run(Long[] jobIds), the job does not run immediately and often run delay 15~20 seconds.
Thread[http-nio-8500-exec-1,5,main]need hello...Mon Jun 27 12:55:02 CST 2022
2022-06-27 12:55:24 INFO [bear] QuartzScheduler_BearScheduler-DESKTOP-521BOOH1656305645549_MisfireHandler org.springframework.scheduling.quartz.LocalDataSourceJobStore Handling 1 trigger(s) that missed their scheduled fire-time.
Thread[BearScheduler_Worker-3,5,main]hello...Mon Jun 27 12:55:24 CST 2022params: aaaa,bbbbbbb
When I delete #Transactional on the run(Long[] jobIds), the job will run immediately.
hread[http-nio-8500-exec-4,5,main]need hello...Mon Jun 27 12:59:40 CST 2022
Thread[BearScheduler_Worker-6,5,main]hello...Mon Jun 27 12:59:40 CST 2022params: aaaa,bbbbbbb
Why my job is handled by MisfireHandler? Why the job can not run immediately when #Transactional on the method? If you know the reason,please help me. Thanks
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
What is the proper way to restart FAILED spring batch job that is using TaskExecutor?
I have a job that is loading data from HTTP and sometimes there is 500 error - making this job fail). I would like to restart this job until it is successful.
If I make JobExecutionListener and implement logic inside afterJob() method, I get error message that this job is actually running. If I use RetryTemplate from Spring, this also doesn't work since this is running inside TaskExecutor.
Any code sample would be of a great help.
Finally I solved the issue by re-implementing JobLauncher:
public class FaultTolerantJobLauncher implements JobLauncher, InitializingBean {
protected static final Log logger = LogFactory.getLog(FaultTolerantJobLauncher.class);
private JobRepository jobRepository;
private RetryTemplate retryTemplate;
private TaskExecutor taskExecutor;
/**
* Run the provided job with the given {#link JobParameters}. The
* {#link JobParameters} will be used to determine if this is an execution
* of an existing job instance, or if a new one should be created.
*
* #param job the job to be run.
* #param jobParameters the {#link JobParameters} for this particular
* execution.
* #return JobExecutionAlreadyRunningException if the JobInstance already
* exists and has an execution already running.
* #throws JobRestartException if the execution would be a re-start, but a
* re-start is either not allowed or not needed.
* #throws JobInstanceAlreadyCompleteException if this instance has already
* completed successfully
* #throws JobParametersInvalidException
*/
#Override
public JobExecution run(final Job job, final JobParameters jobParameters)
throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException,
JobParametersInvalidException {
Assert.notNull(job, "The Job must not be null.");
Assert.notNull(jobParameters, "The JobParameters must not be null.");
final AtomicReference<JobExecution> executionReference = new AtomicReference<>();
JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters);
if (lastExecution != null) {
if (!job.isRestartable()) {
throw new JobRestartException("JobInstance already exists and is not restartable");
}
/*
* validate here if it has stepExecutions that are UNKNOWN, STARTING, STARTED and STOPPING
* retrieve the previous execution and check
*/
for (StepExecution execution : lastExecution.getStepExecutions()) {
BatchStatus status = execution.getStatus();
if (status.isRunning() || status == BatchStatus.STOPPING) {
throw new JobExecutionAlreadyRunningException("A job execution for this job is already running: "
+ lastExecution);
} else if (status == BatchStatus.UNKNOWN) {
throw new JobRestartException(
"Cannot restart step [" + execution.getStepName() + "] from UNKNOWN status. "
+ "The last execution ended with a failure that could not be rolled back, "
+ "so it may be dangerous to proceed. Manual intervention is probably necessary.");
}
}
}
// Check the validity of the parameters before doing creating anything
// in the repository...
job.getJobParametersValidator().validate(jobParameters);
taskExecutor.execute(new Runnable() {
#Override
public void run() {
try {
retryTemplate.execute(new FaultTolerantJobRetryCallback(executionReference, job, jobParameters));
} catch (TaskRejectedException e) {
executionReference.get().upgradeStatus(BatchStatus.FAILED);
if (executionReference.get().getExitStatus().equals(ExitStatus.UNKNOWN)) {
executionReference.get().setExitStatus(ExitStatus.FAILED.addExitDescription(e));
}
jobRepository.update(executionReference.get());
}
}
});
return executionReference.get();
}
/**
* Set the JobRepsitory.
*
* #param jobRepository
*/
public void setJobRepository(JobRepository jobRepository) {
this.jobRepository = jobRepository;
}
/**
* Set the retryTemplate
*
* #param retryTemplate
*/
public void setRetryTemplate(RetryTemplate retryTemplate) {
this.retryTemplate = retryTemplate;
}
/**
* Set the TaskExecutor. (Optional)
*
* #param taskExecutor
*/
public void setTaskExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
/**
* Ensure the required dependencies of a {#link JobRepository} have been
* set.
*/
#Override
public void afterPropertiesSet() throws Exception {
Assert.state(jobRepository != null, "A JobRepository has not been set.");
Assert.state(retryTemplate != null, "A RetryTemplate has not been set.");
if (taskExecutor == null) {
logger.info("No TaskExecutor has been set, defaulting to synchronous executor.");
taskExecutor = new SyncTaskExecutor();
}
}
private class FaultTolerantJobRetryCallback implements RetryCallback<Object, RuntimeException> {
private final AtomicReference<JobExecution> executionReference;
private final Job job;
private final JobParameters jobParameters;
FaultTolerantJobRetryCallback(AtomicReference<JobExecution> executionReference, Job job, JobParameters jobParameters){
this.executionReference = executionReference;
this.job = job;
this.jobParameters = jobParameters;
}
#Override
public Object doWithRetry(RetryContext retryContext) {
if(!job.isRestartable()){
//will be set only once and in case that job can not be restarted we don't retry
retryContext.setExhaustedOnly();
}
if(retryContext.getRetryCount() > 0){
logger.info("Job: [" + job + "] retrying/restarting with the following parameters: [" + jobParameters
+ "]");
}
try {
/*
* There is a very small probability that a non-restartable job can be
* restarted, but only if another process or thread manages to launch
* <i>and</i> fail a job execution for this instance between the last
* assertion and the next method returning successfully.
*/
executionReference.set(jobRepository.createJobExecution(job.getName(), jobParameters));
logger.info("Job: [" + job + "] launched with the following parameters: [" + jobParameters
+ "]");
job.execute(executionReference.get());
logger.info("Job: [" + job + "] completed with the following parameters: [" + jobParameters
+ "] and the following status: [" + executionReference.get().getStatus() + "]");
}
catch (JobInstanceAlreadyCompleteException | JobExecutionAlreadyRunningException e){
retryContext.setExhaustedOnly(); //don't repeat if instance already complete or running
rethrow(e);
}
catch (Throwable t) {
logger.info("Job: [" + job
+ "] failed unexpectedly and fatally with the following parameters: [" + jobParameters
+ "]", t);
rethrow(t);
}
if(job.isRestartable() && executionReference.get().getStatus() == BatchStatus.FAILED){
//if job failed and can be restarted, use retry template to restart the job
throw new TaskRejectedException("RetryTemplate failed too many times");
}
return null;
}
private void rethrow(Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
else if (t instanceof Error) {
throw (Error) t;
}
throw new IllegalStateException(t);
}
}
}
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.
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.