Scenario : I want to create a scheduler application which should run shell scripts as per the defined schedule. To keep it simple, I want the user to add script name and execution timings in some external file (properties/xml) which will be used by my application. For now, I am planning to run this application as a background process on Linux server. In future may be we'll make it as a web-app.
What I've tried till now:
I came across xmlschedulingdataprocessorplugin for this purpose but it requires user to write jobs as Java code and then add it in XML file.
I found some examples for scheduling which presently isn't working.
Please suggest some helpful quartz API which can help me in fulfilling this purpose.
UPDATE:
public class CronTriggerExample {
public static void main(String[] args) throws Exception {
String[] a = {"script1.sh:0/10 * * * * ?", "script2.sh:0/35 * * * * ?"};
for (String config : a) {
String[] attr = config.split(":");
System.out.println("Iterating for : "+attr[0]);
JobKey jobKey = new JobKey(attr[0], attr[0]);
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity(attr[0], attr[0])
.withSchedule(CronScheduleBuilder.cronSchedule(attr[1]))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getContext().put("val", config);
JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity(jobKey).build();
scheduler.start();
scheduler.scheduleJob(job, trigger);
System.out.println("=======================");
}
}
}
My HelloJob class:
public class HelloJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
String objectFromContext = null;
Date date = new Date();
try {
SchedulerContext schedulerContext = context.getScheduler().getContext();
objectFromContext = (String) schedulerContext.get("val");
} catch (SchedulerException ex) {
ex.printStackTrace();
}
System.out.println("Triggered "+objectFromContext+" at: "+date);
}
}
OUTPUT:
Iterating for : script1.sh
log4j:WARN No appenders could be found for logger (org.quartz.impl.StdSchedulerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
=======================
Iterating for : script2.sh
=======================
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:21:50 IST 2016
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:22:00 IST 2016
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:22:00 IST 2016
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:22:10 IST 2016
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:22:20 IST 2016
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:22:30 IST 2016
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:22:35 IST 2016
Triggered script2.sh:0/35 * * * * ? at: Mon Apr 18 12:22:40 IST 2016
What am I missing? I tried to create new Job for each iteration and assign script names as JobExecutionContext
The below tutorial help you to schedule shell script.
http://www.mkyong.com/java/how-to-run-a-task-periodically-in-java/
By using
Runtime.getRuntime().exec("sh shellscript.sh");
You can run shell script.
I would take the following approach :
You create a class JobShellRunner which will implement Job interface from quartz :
public class JobShellRunner implements Job {
#Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
// here you take need information about the shell script you need to run, from the context and run the shell script
}
}
read properties files (i suppose here it will be available information about which shell script to run and it's schedule information). For each information needed you will create it's context and it's trigger:
JobKey jobKey = new JobKey("jobShellRunner", "group1");
// put in the job key need information about shell script (path, etc)
JobDetail jobA = JobBuilder.newJob(JobShellRunner.class)
.withIdentity(jobKey).build();
Then the trigger (note that on the cron expression you should complete with the one that you read from properties file):
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("dummyTriggerName1", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
Then schedule the job
scheduler.scheduleJob(jobA, trigger);
you can do something like as following way,
First of need to go likewise,
create your java application which has one scheduled-job which will read at
some time interval one propery/xml file which will provide an information
for which shell_file needs to execute and at what time.
While your program's scheduled-job read that property/xml file and getting
information as following,
2.1. Shell-Script-File-Name
2.2. Timing at what time that script needs to be execute.
This information which is read by above(step-2), with help of it, this job
will create newer independent-job which is fully responsible for execute
shell script at particular time.(that time will be your job-time which is
read from your propery/xml file). also take care of it to it should be one time only(as per your requirement).
this above step repeatedly does untill whole information read by this job and every time will generate one newer job.
in case after some time user edit/updated/added new line into property/xml file this java program's scheduled job will read only
that newer changes and accordingly does as like above explained.
you can see below image for better understanding purpose,
For scheduling purpose you can set-up spring-quartz API for schedule job.
here, I am going to give you little bit pseudo code,
public class JobA implements Job {
#Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
// continues read property/xml file untill while file not read
// based upon above read info. generate new job(one time only) at runtime which has capability to execute shell script
// shell script can be execute by java program by this ,
// Runtime.getRuntime().exec("sh /full-path/shell_script_name.sh");
}
}
............
public class CronTriggerExample {
public static void main( String[] args ) throws Exception
{
JobKey jobKeyA = new JobKey("jobA", "group1");
JobDetail jobA = JobBuilder.newJob(JobA.class)
.withIdentity(jobKeyA).build();
Trigger trigger1 = TriggerBuilder
.newTrigger()
.withIdentity("dummyTriggerName1", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) // you can set here your comfortable job time...
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(jobA, trigger1);
}
}
So, this is an idea what I believe and represent over here, which is top-most suitable as per your requirement.
Related
public class QuartzStudy implements Job {
#Override
public void execute(JobExecutionContext jobExecutionContext) {
System.out.println(jobExecutionContext.getJobDetail().getKey().getName() + "-" + Thread.currentThread().getName() + "-" + Thread.currentThread().getId() + "-" + new Date());
}
private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
private static Scheduler scheduler;
static {
try {
scheduler = schedulerFactory.getScheduler();
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws SchedulerException, InterruptedException {
JobDetail jobDetail = JobBuilder.newJob(QuartzStudy.class).withIdentity("job1").build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?").withMisfireHandlingInstructionIgnoreMisfires())
.build();
scheduler.scheduleJob(jobDetail, trigger);
Thread.sleep(10000);
scheduler.pauseJob(jobDetail.getKey());
}
}
This code should console twice and then pause?
But sometimes console three times,why?
job1-DefaultQuartzScheduler_Worker-1-14-Thu Jan 03 09:34:45 CST 2019
job1-DefaultQuartzScheduler_Worker-2-15-Thu Jan 03 09:34:50 CST 2019
job1-DefaultQuartzScheduler_Worker-3-16-Thu Jan 03 09:34:55 CST 2019
First of all, */5 * * * * ? means Every 5 seconds starting at :00 second after the minute.
You are doing Thread.sleep(10000);. This means main thread will be paused for 10 seconds. But, since you are getting logs even in those 10 seconds, that means scheduler is independent of main thread. So, at 10th second, scheduler will create a thread to run execute and also after 10th second, you are pausing job.If first one(i.e. creating of thread occurs before pausing) occurs first, logs will be printed thrice. Whereas if second one(pausing of job occurs before spawning new thread) occurs first, logs will be printed only twice. That's why sometime log is getting printed twice and sometimes thrice.
So, to be sure that logs are printed only twice, decrease sleep time to 9 seconds. I tried below code and it always prints logs twice:
public static void main(String[] args) throws SchedulerException, InterruptedException {
System.out.println("Main method:" + Thread.currentThread().getName() + "-" + Thread.currentThread().getId());
JobDetail jobDetail = JobBuilder.newJob(QuartzStudy.class).withIdentity("job1").build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?").withMisfireHandlingInstructionIgnoreMisfires())
.build();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start(); // it's better to start job once you have scheduled one
Thread.sleep(9000);
scheduler.pauseJob(jobDetail.getKey());
System.out.println("Job is Paused!!!");
}
I would like to run cron expression at 11:10 pm of very month ending. I am trying to write by following link A Guide To Cron Expressions but I am unable to write. Sample code is below is this will work fine.
#Scheduled(cron = "0 10 23 L * ? *")
public void scheduleTaskWithCronExpression() {
System.out.println("Cron Task");
}
Try with this:
#Scheduled(cron = "0 10 23 L * ?")
For generate or check corn expression use this
You can also try with time zone
#Scheduled(cron = "0 10 23 L * ?", zone = "CET")
public void scheduleTaskWithCronExpression() {
System.out.println("Cron Task");
}
In my quartz scheduler my Main class is executed 5 or 6 times when a cron trigger firedin every 20 seconds.I want to restrict to execute Main class only once when cron trigger fired in every 20 seconds
This is my CronTrigger
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
JobDetail job = JobBuilder.newJob(Main.class).build();
Date startTime = DateBuilder.nextGivenSecondDate(null, 10);
// run every 20 seconds infinite loop
CronTrigger crontrigger = TriggerBuilder.newTrigger().startAt(startTime).withSchedule(CronScheduleBuilder.cronSchedule("0/20 * * * * ?")).build();
scheduler.start();
scheduler.scheduleJob(job, crontrigger);
This is Main Class method
Main.java
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Trigger Starts..");
}
My actual output is
Trigger 1 - When cron fires first time it will execute 3 or 4 times
Trigger Starts..
Trigger Starts..
Trigger Starts..
Trigger 2 - When cron fires second time it will execute 6 or 7 times
Trigger Starts..
Trigger Starts..
Trigger Starts..
Trigger Starts..
Trigger Starts..
Trigger Starts..
Trigger Starts..
Trigger Starts..
6 or 7 times this Main class is called. I want to restrict this situation
My expected output is
Trigger 1 (first 20 sec)
Trigger Starts..
Trigger 2 (next 20 sec)
Trigger Starts..
It should be executed once
On my system, I tested it and it works perfectly fine. Can you please use the following template and please share it's output and some info about logger if used.
Job:
public void execute(JobExecutionContext context) throws JobExecutionException
{
int count;
if(context.get("count") == null)
context.put("count", count = 1);
else
context.put("count", count = (Integer) context.get("count") + 1);
System.out.println("Hello Quartz!, Count: " + count);
}
This is my CronTrigger:
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
JobDetail job = JobBuilder.newJob(HelloJob.class).build();
// run every 20 seconds infinite loop
CronTrigger crontrigger = TriggerBuilder.newTrigger().withSchedule(
CronScheduleBuilder.cronSchedule("0/20 * * * * ?")).build();
scheduler.start();
scheduler.scheduleJob(job, crontrigger);
I am trying to update the quartz job scheduling dynamically:
Initially it is after every 3 minute, but dynamically i want to make it 1 min.
here's my quartz.xml and java file
quartz.xml
<schedule>
<job>
<name>JOBNAME</name>
<group>GroupDummy</group>
<description>This is Job B</description>
<job-class>in.xyz.MyClass.java</job-class>
</job>
<trigger>
<cron>
<name>dummyTriggerNameB</name>
<group>MYTRIGGER_GROUP</group>
<job-name>JOBNAME</job-name>
<job-group>GroupDummy</job-group>
<cron-expression>0 0/3 * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
MyClass.java
public void execute(JobExecutionContext arg0) throws JobExecutionException {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger("dummyTriggerNameB", "MYTRIGGER_GROUP");
CronExpression cronExpression = new CronExpression("0 0/1 * * * ?");
cronTrigger.setCronExpression(cronExpression);
scheduler.rescheduleJob("dummyTriggerNameB", "MYTRIGGER_GROUP", cronTrigger);
}
I am trying the above java code to change the time from 3 minute to 1 minute dynamically but it is not happening "cronTrigger" is returning null.
Help to solve this problem so that i can reschedule dynamically.
Ok, so here I've got a Quartz(version 2.2.1) job that should instantiate two other jobs, create and add two triggers for each job, and add both jobs to the scheduler, deleting them after a set interval that's passed in through the JobDataMap.
When this job is run by the scheduler, it throws the following exception:
org.quartz.JobPersistenceException: The job (DEFAULT.countdown15, countdownGroup) referenced by the trigger does not exist.
The exception is thrown at line 71:
sched.scheduleJob(countdown15Trigger2);
I couldn't figure out why the job doesn't exist at that point, since it should have been created by the earlier newJob call and added to the scheduler by line 70:
sched.scheduleJob(countdown15, countdown15Trigger1);
So, I temporarily commented out that line and attempted to run the program again, whereupon I got this exception at line 72:
line 72:
sched.scheduleJob(countdown60, countdown60Trigger1);
exception:
org.quartz.SchedulerException: Trigger does not reference given job!
Ok, so now I've got two scheduleJob calls throwing two different exceptions, even though as far as I can tell I'm following the correct syntax. But the first scheduleJob call must be working correctly, because the exceptions are occurring later in the code, so for now I'll comment out all the other scheduleJob calls and see what happens.
And then I get this error at line 70, the first and now only scheduleJob call:
org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'countdownGroup.countdown15', because one already exists with this identification.
I don't really know where to go from here, so here's the complete code for the top level job. Any ideas?
public class ShowClockJob implements Job {
public ShowClockJob() {}
#Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
JobDataMap dm = context.getJobDetail().getJobDataMap();
int min = dm.getInt("min"); //show's runtime in minutes
int ms = min * 60000; //convert the runtime in minutes to millisec
Scheduler sched = context.getScheduler();
JobDetail countdown15 = newJob(CountdownJob15.class)
.withIdentity(jobKey("countdown15", "countdownGroup"))
.build();
JobDetail countdown60 = newJob(CountdownJob60.class)
.withIdentity("countdown60", "countdownGroup")
.build();
Trigger countdown15Trigger1 = newTrigger()
.withIdentity("countdown15Trigger1", "countdownGroup")
.withSchedule(cronSchedule("45 5 * * * ?")
.withMisfireHandlingInstructionDoNothing())
.forJob(jobKey("countdown15", "countdownGroup"))
.build();
Trigger countdown15Trigger2 = newTrigger()
.withIdentity("countdown15Trigger2", "countdownGroup")
.withSchedule(cronSchedule("55 19,32,46 * * * ?")
.withMisfireHandlingInstructionDoNothing())
.forJob(jobKey("countdown15, countdownGroup"))
.build();
Trigger countdown60Trigger1 = newTrigger()
.withIdentity("countdown60Trigger1", "countdownGroup")
.withSchedule(cronSchedule("0 17,29,44 * * * ?")
.withMisfireHandlingInstructionDoNothing())
.forJob(jobKey("countdown60, countdownGroup"))
.build();
Trigger countdown60Trigger2 = newTrigger()
.withIdentity("countdown60Trigger2", "countdownGroup")
.withSchedule(cronSchedule("50 57 * * * ?")
.withMisfireHandlingInstructionDoNothing())
.forJob("countdown60, countdownGroup")
.build();
try {
sched.scheduleJob(countdown15, countdown15Trigger1);
sched.scheduleJob(countdown15Trigger2);
sched.scheduleJob(countdown60, countdown60Trigger1);
sched.scheduleJob(countdown60Trigger2);
Thread.sleep(ms); //sleep for the length of the showtime,
//...then shut down the countdown jobs
sched.deleteJob(jobKey("countdown15","countdownGroup"));
sched.deleteJob(jobKey("countdown60","countdownGroup"));
} catch (SchedulerException | InterruptedException ex) {
Logger.getLogger(ShowClockJob.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Turns out it was a simple syntax error. In all but the first trigger creation blocks, at the forJob method, I left out the middle two parenthesis that seperated the jobKey and jobGroup into two strings.
So this:
forJob("countdown60, countdownGroup");
Should have been this:
forJob("countdown60", "countdownGroup");