Quartz CronTrigger executing jobs on wrong date/time - java

I am using following cron expression to execute a job on every Friday at specified time of day (in sample below it's 1:13 PM).
0 13 13 ? * FRI
So expected behaviour should be if I initialize this trigger any day other then Friday then it should not start executing until next Friday. But whats happening in my case is even if I initialized this trigger today (as today is Wednesday), it starts executing jobs at the very moment.
Relevant java source:
CronTrigger cronTrigger = new CronTrigger("trigger_" + groupName, groupName, cronExpression);
cronTrigger.setStartTime(startDate); //startDate = 1-Mar-2012
cronTrigger.setEndTime(endDate); //endDate = 30-Apr-2012

Your issue is configuring the startTime. startTime is meant to be the time at at which the trigger should occur. Since the date is old this causes a misfire in the scheduler and the default behavior is for the scheduler to immediately refire.
Remove setStartTime, the default behavior is for startTime to be set to the current time and the first trigger time will be the match to the cron trigger after the start time so this Thursday.
Quick little test I through together to verify:
public class Test {
public static void main(String[] args) throws ParseException, SchedulerException {
String groupName = "group";
String cronExpression = "0 13 13 ? * THUR";
CronTrigger cronTrigger = new CronTrigger("trigger_" + groupName, groupName, cronExpression);
cronTrigger.setStartTime(new Date(0));
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
JobDetail detail = new JobDetail("testJob", groupName, TestJob.class);
scheduler.scheduleJob(detail, cronTrigger);
scheduler.start();
try {
Thread.sleep(50001);
} catch (Exception ignore) {
}
}
public static class TestJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("TEST");
}
}
}
When removing the setStartTime my print message does not trigger. With it there the print message triggers.

Related

Updating CroneSchedule & Job Dynamically without stopping server

I need help in externalizing the below cron schedule or executing it dynamically. For example, in the below code it is hardcoded to perform every Saturday: cronSchedule("0 0 12 ? * SAT"). I want the value inside the cronSchedule() to externalize so that even after the server is started, I can still change the cron schedule to Monday or every day based on my choice and it can be run. I am looking for suggestion in java and not in spring.
public void run() throws Exception {
// Getting a reference to a scheduler
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
// job will run every week at Saturday 12 Noon Server Time
JobDetail job = newJob(CachingJob.class).withIdentity("job1", "group1").build();
CronTrigger trigger = newTrigger().withIdentity("trigger1", "group1").withSchedule(cronSchedule("0 0 12 ? * SAT"))
.build();
Date ft = sched.scheduleJob(job, trigger);
sched.start();
SchedulerMetaData metaData = sched.getMetaData();
}
Any input or suggestion is appreciated.
We can perform below method to reschedule the job :
cronScheduler.rescheduleJob(cronTrigger.getKey(), newTrigger().withIdentity("customTrigger", "defaultGroup")
.withSchedule(cronSchedule(cronExpression)).build());

Schedule Cron job to trigger the 1st day of month -- except if it's Sunday

I want it fire a job once on every 1st day in a every month that should be any week day.But if it is sunday on 1st do not fire the job just postpone it to fire at 2nd day and immediately terminate schedule.
Again do the same process of scheduling for next months also.?
Below is the code to schedule it for every 55 seconds for testing envrironment and how about the destroy method?
QuartzPlugin.java
public class QuartzPlugin implements PlugIn {
#Override
public void destroy() {
}
#Override
public void init(ActionServlet servlet, ModuleConfig config) throws ServletException {
// define the job and tie it to our MyJob class
JobDetail job = JobBuilder.newJob(SchedulerJob.class).withIdentity("anyJobName", "group1").build();
try {
// Cron Trigger the job to run now, and then repeat every 55 secs
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("anyTriggerName", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/55 * * * * ?"))
//Every 1 minute "0 0/1 * 1/1 * ? *"
// Every 55 Sec "0/55 * * * * ?"
// Every 5 Sec "0/5 * * * * ?"
// Every month 1st day "0 10 1-7 * *"
.build();
// Grab the Scheduler instance from the Factory
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
// and start it off
scheduler.start();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
SchedularJob.java
public class SchedulerJob implements Job {
private ArrayList mapList = new ArrayList();
private DAO dao = new DAO();
private HSSFWorkbook workbook = new HSSFWorkbook();
private FileOutputStream fileOut = null;
public void execute(JobExecutionContext context) throws JobExecutionException {
String dirpath = System.getProperty("user.dir").replaceAll("bin", "") + "webapps/it_email/sent/email_"
+ new SimpleDateFormat("dd-MM-yyyy").format(new Date());
String genStatus = generateExcel(dirpath);
System.out.println("genStatus::" + genStatus);
if (genStatus.equals("Y")) {
String text = "Please find attached case list dated "
+ new SimpleDateFormat("dd-MM-yyyy").format(new Date()) + "."
+ "\n\n Microsoft Excel is required to open this attachment.\n\n"
+ "Registrar Judicial,High Court of Judicature at Hyderabad.\n\n"
+ "This email is System generated. Please do not reply to this email ID.\n\n "
+ "Disclaimer:The NIC/High Court is not responsible for non-delivery of emails.";
String subject = "HIGH COURT:Availability of Case Data.";
String to = "ajaythakur2014#gmail.com";
String filetype = ".xls";
sendEmail(to, subject, text, dirpath, filetype);
System.out.println("For every 55 secs");
}
}
When the above code is run i sporadically get the
Oracle 9i Production Environment : IO Exception : The Network Adapter could not establish the connection
i guess creating more no of new connections every 55 seconds.
Could be useful this link ,where there is the official documentation :MonthlyTrigger

Java Quartz scheduler trigger nextFireTime is null

I am creating a Quartz trigger for various schedule frequencies.
At the end of the function I return the trigger, but debug output it before the return statement.
The debug output is this Trigger 'DEFAULT.6da64b5bd2ee-91b0fa99-6e11-4356-a55f-e80353b61fc4': triggerClass: 'org.quartz.impl.triggers.DailyTimeIntervalTriggerImpl calendar: 'null' misfireInstruction: -1 nextFireTime: null.
I am worried about nextFireTime being null, will the trigger still fire?
Part of the code looks like this:
if (freqType.equalsIgnoreCase(KpiDefinition.KPI_FREQ_TYPE_DAILY)
|| freqType
.equalsIgnoreCase(KpiDefinition.KPI_FREQ_TYPE_SCHEDULED))
{
DailyTimeIntervalScheduleBuilder schedule = DailyTimeIntervalScheduleBuilder
.dailyTimeIntervalSchedule()
.startingDailyAt(startTime)
.endingDailyAt(endTime)
.onEveryDay()
.withInterval(kpiDef.getKpiFrequency().intValue(),
IntervalUnit.valueOf(kpiDef.getKpiFreqTimeUnit()))
.withMisfireHandlingInstructionIgnoreMisfires();
Date startDate = kpiDef.getKpiStartDate();
if (startDate.before(new Date()))
{
startDate = new Date();
}
if (freqType.equalsIgnoreCase(KpiDefinition.KPI_FREQ_TYPE_DAILY))
{
trigger = newTrigger().withSchedule(schedule)
.startAt(startDate).build();
} else if (freqType
.equalsIgnoreCase(KpiDefinition.KPI_FREQ_TYPE_SCHEDULED))
{
Date endDate = kpiDef.getKpiEndDate();
// This means that schedule has already passed and so KPI should
// not be scheduled.
if (endDate.before(new Date()))
{
logger.debug("getTriggerWithSchedule for KPI " + kpiDef.getKpiDefId() + " null Schedule returned for end date " +endDate.toString());
return null;
}
trigger = newTrigger().withSchedule(schedule)
.startAt(startDate).endAt(endDate).build();
}
}
nextFireTime is only resolved for CronTriggers in my experience. SimpleTrigger for example also has nextFireTime=null. So I wouldn't worry.
Bear in mind also this:
The value returned is not guaranteed to be valid until after the Trigger has been added to the scheduler.
http://www.quartz-scheduler.org/api/2.1.7/org/quartz/Trigger.html#getNextFireTime()

Timer and TimerTask not update

I have an app that could set the time for processing. The problems is when I update the time, the processing will increase. For example:
Initially the timer start at 07:00AM
Let say, I update the timer to 08:00AM then the next day onwards, the program will run again at 07:00AM and also at 08:00AM. (The 07:00AM is still in scheduler, how to remove the 07:00AM?)
How to make the scheduler to only run the 08:00AM the next day?
public void setKonfigurasi(String name, String value) {
log.info(SERVLET_NAME + "Entering setKonfigurasi");
amBean.setParam(name,value); //update the time into database
//name = 'processFileConf|kodPT|userA|20140312 08:30 AM'
// reschedule timer after configured by user
try {
String kodPT = name.substring(name.indexOf("|") + 1, name.indexOf("|",name.indexOf("|") + 1));
String configStr = value.substring(2); //get the new time
String currentStr = CommonUtil.getCurrentDate();
DateFormat dateformat = new SimpleDateFormat("dd/MM/yyyy KK:mm:ss a");
Date currentDate=new Date() ;
Date configDate = dateformat.parse(currentStr+" "+configStr);
long config = configDate.getTime();
long current = currentDate.getTime();
// today
long delay = config-current;
if (delay < 0)
// tomorrow
delay += (1000*60*60*24);
// create the timer and timer task objects
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
System.out.println("showtime for "+kodPT);
processFile("auto"+kodPT);
}
}, delay, 1000*60*60*24);
ServletContextEvent servletContextEvent = EtServletContextListener.getContext();
ServletContext servletContext = servletContextEvent.getServletContext();
servletContext.removeAttribute ("timer");
servletContext.setAttribute ("timer", timer);
} catch (Exception e) {
log.error("Exception on date format : "+e.getMessage());
}
log.info(SERVLET_NAME + "Exiting setKonfigurasi");
}
You need to call cancel() on the previous timer and create a new one. The javadoc says:
Terminates this timer, discarding any currently scheduled tasks. Does
not interfere with a currently executing task (if it exists). Once a
timer has been terminated, its execution thread terminates gracefully,
and no more tasks may be scheduled on it.
I have found out about how to do what I want. Instead of using java.util.Timer to create the timer, we should use javax.ejb.Timer since the Timer in ejb have an info to identified each timer.
Timer Service EJB

Quartz retry when failure

Let's say I have a trigger configured this way:
<bean id="updateInsBBTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="updateInsBBJobDetail"/>
<!-- run every morning at 5 AM -->
<property name="cronExpression" value="0 0 5 * * ?"/>
</bean>
The trigger have to connect with another application and if there is any problem (like a connection failure) it should to retry the task up to five times every 10 minutes or until success. There is any way to configure the trigger to work like this?
I would recommend an implementation like this one to recover the job after a fail:
final JobDataMap jobDataMap = jobCtx.getJobDetail().getJobDataMap();
// the keys doesn't exist on first retry
final int retries = jobDataMap.containsKey(COUNT_MAP_KEY) ? jobDataMap.getIntValue(COUNT_MAP_KEY) : 0;
// to stop after awhile
if (retries < MAX_RETRIES) {
log.warn("Retry job " + jobCtx.getJobDetail());
// increment the number of retries
jobDataMap.put(COUNT_MAP_KEY, retries + 1);
final JobDetail job = jobCtx
.getJobDetail()
.getJobBuilder()
// to track the number of retries
.withIdentity(jobCtx.getJobDetail().getKey().getName() + " - " + retries, "FailingJobsGroup")
.usingJobData(jobDataMap)
.build();
final OperableTrigger trigger = (OperableTrigger) TriggerBuilder
.newTrigger()
.forJob(job)
// trying to reduce back pressure, you can use another algorithm
.startAt(new Date(jobCtx.getFireTime().getTime() + (retries*100)))
.build();
try {
// schedule another job to avoid blocking threads
jobCtx.getScheduler().scheduleJob(job, trigger);
} catch (SchedulerException e) {
log.error("Error creating job");
throw new JobExecutionException(e);
}
}
Why?
It will not block Quartz Workers
It will avoid back pressure. With setRefireImmediately the job will be fired immediately and it could lead to back pressure issues
Source: Automatically Retry Failed Jobs in Quartz
If you want to have a job which keeps trying over and over again until it succeeds, all you have to do is throw a JobExecutionException with a flag to tell the scheduler to fire it again when it fails. The following code shows how:
class MyJob implements Job {
public MyJob() {
}
public void execute(JobExecutionContext context) throws JobExecutionException {
try{
//connect to other application etc
}
catch(Exception e){
Thread.sleep(600000); //sleep for 10 mins
JobExecutionException e2 = new JobExecutionException(e);
//fire it again
e2.setRefireImmediately(true);
throw e2;
}
}
}
It gets a bit more complicated if you want to retry a certain number of times. You have to use a StatefulJob and hold a retryCounter in its JobDataMap, which you increment if the job fails. If the counter exceeds the maximum number of retries, then you can disable the job if you wish.
class MyJob implements StatefulJob {
public MyJob() {
}
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
int count = dataMap.getIntValue("count");
// allow 5 retries
if(count >= 5){
JobExecutionException e = new JobExecutionException("Retries exceeded");
//make sure it doesn't run again
e.setUnscheduleAllTriggers(true);
throw e;
}
try{
//connect to other application etc
//reset counter back to 0
dataMap.putAsString("count", 0);
}
catch(Exception e){
count++;
dataMap.putAsString("count", count);
JobExecutionException e2 = new JobExecutionException(e);
Thread.sleep(600000); //sleep for 10 mins
//fire it again
e2.setRefireImmediately(true);
throw e2;
}
}
}
I would suggest for more flexibility and configurability to better store in your DB two offsets: the repeatOffset which will tell you
after how long the job should be retried and the trialPeriodOffset which will keep the information of the time window that the job is
allowed to be rescheduled. Then you can retrieve these two parameters like (I assume you are using Spring):
String repeatOffset = yourDBUtilsDao.getConfigParameter(..);
String trialPeriodOffset = yourDBUtilsDao.getConfigParameter(..);
Then instead of the job to remember the counter it will need to remember the initalAttempt:
Long initialAttempt = null;
initialAttempt = (Long) existingJobDetail.getJobDataMap().get("firstAttempt");
and perform the something like the following check:
long allowedThreshold = initialAttempt + Long.parseLong(trialPeriodOffset);
if (System.currentTimeMillis() > allowedThreshold) {
//We've tried enough, time to give up
log.warn("The job is not going to be rescheduled since it has reached its trial period threshold");
sched.deleteJob(jobName, jobGroup);
return YourResultEnumHere.HAS_REACHED_THE_RESCHEDULING_LIMIT;
}
It would be a good idea to create an enum for the result of the attempt that is being returned back to the core workflow of your
application like above.
Then construct the rescheduling time:
Date startTime = null;
startTime = new Date(System.currentTimeMillis() + Long.parseLong(repeatOffset));
String triggerName = "Trigger_" + jobName;
String triggerGroup = "Trigger_" + jobGroup;
Trigger retrievedTrigger = sched.getTrigger(triggerName, triggerGroup);
if (!(retrievedTrigger instanceof SimpleTrigger)) {
log.error("While rescheduling the Quartz Job retrieved was not of SimpleTrigger type as expected");
return YourResultEnumHere.ERROR;
}
((SimpleTrigger) retrievedTrigger).setStartTime(startTime);
sched.rescheduleJob(triggerName, triggerGroup, retrievedTrigger);
return YourResultEnumHere.RESCHEDULED;

Categories

Resources