/**
* Copyright 2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nutch.admin.scheduling;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* Scheduling service which uses the quartz scheduling framework. For example
* and explanation of <i>cronExpression</i>s which are used to initialize cron
* jobs see below(taken from quartz documentation). <p/>
*
* A cron expression has 6 mandatory and 1 optional elements separate with white
* space. <br/> "sec min hour dayOfMonth Month dayOfWeek Year(optional)" <p/>
*
* An example of a complete cron-expression is the string "0 0 12 ? * WED" which
* means "every Wednesday at 12:00 pm". <br>
* <br>
*
* Individual sub-expressions can contain ranges and/or lists. For example, the
* day of week field in the previous (which reads "WED") example could be
* replaces with "MON-FRI", "MON, WED, FRI", or even "MON-WED,SAT". <br>
* <br>
*
* Wild-cards (the '*' character) can be used to say "every" possible value of
* this field. Therefore the '*' character in the "Month" field of the previous
* example simply means "every month". A '*' in the Day-Of-Week field would
* obviously mean "every day of the week". <br>
* <br>
*
* All of the fields have a set of valid values that can be specified. These
* values should be fairly obvious - such as the numbers 0 to 59 for seconds and
* minutes, and the values 0 to 23 for hours. Day-of-Month can be any value
* 0-31, but you need to be careful about how many days are in a given month!
* Months can be specified as values between 0 and 11, or by using the strings
* JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC. Days-of-Week
* can be specified as values between 1 and 7 (1 = Sunday) or by using the
* strings SUN, MON, TUE, WED, THU, FRI and SAT. <br>
* <br>
*
* The '/' character can be used to specify increments to values. For example,
* if you put '0/15' in the Minutes field, it means 'every 15 minutes, starting
* at minute zero'. If you used '3/20' in the Minutes field, it would mean
* 'every 20 minutes during the hour, starting at minute three' - or in other
* words it is the same as specifying '3,23,43' in the Minutes field. <br>
* <br>
*
* The '?' character is allowed for the day-of-month and day-of-week fields. It
* is used to specify "no specific value". This is useful when you need to
* specify something in one of the two fields, but not the other. See the
* examples below for clarification. <br>
* <br>
*
* The 'L' character is allowed for the day-of-month and day-of-week fields.
* This character is short-hand for "last", but it has different meaning in each
* of the two fields. For example, the value "L" in the day-of-month field means
* "the last day of the month" - day 31 for January, day 28 for February on
* non-leap years. If used in the day-of-week field by itself, it simply means
* "7" or "SAT". But if used in the day-of-week field after another value, it
* means "the last xxx day of the month" - for example "6L" or "FRIL" both mean
* "the last Friday of the month". When using the 'L' option, it is important
* not to specify lists, or ranges of values, as you'll get confusing results.
* <br>
* <br>
*
* "0 0/25 * * * ?" Fire every 25 minutes<br>
*
* "10 0/5 * * * ?" Fires every 5 minutes, at 10 seconds after the minute (i.e.
* 10:00:10 am, 10:05:10 am, etc.).</br>
*
* "0 0 12 * * ?" Fire at 12pm (noon) every day<br>
*
* "0 15 10 ? * *" Fire at 10:15am every day<br>
*
* "0 15 10 * * ?" Fire at 10:15am every day<br>
*
* "0 15 10 * * ? *" Fire at 10:15am every day<br>
*
* "0 15 10 * * ? 2005" Fire at 10:15am every day during the year 2005<br>
*
* "0 * 14 * * ?" Fire every minute starting at 2pm and ending at 2:59pm, every
* day<br>
*
* "0 0/5 14 * * ?" Fire every 5 minutes starting at 2pm and ending at 2:55pm,
* every day<br>
*
* "0 0/5 14,18 * * ?" Fire every 5 minutes starting at 2pm and ending at
* 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every
* day<br>
*
* "0 0-5 14 * * ?" Fire every minute starting at 2pm and ending at 2:05pm,
* every day<br>
*
* "0 10,44 14 ? 3 WED" Fire at 2:10pm and at 2:44pm every Wednesday in the
* month of March.<br>
*
* "0 15 10 ? * MON-FRI"Fire at 10:15am every Monday, Tuesday, Wednesday,
* Thursday and Friday<br>
*
* "0 15 10 15 * ?" Fire at 10:15am on the 15th day of every month<br>
*
* "0 15 10 L * ?" Fire at 10:15am on the last day of every month<br>
*
* "0 15 10 ? * 6L" Fire at 10:15am on the last Friday of every month<br>
*
* "0 15 10 ? * 6L" Fire at 10:15am on the last Friday of every month<br>
*
* "0 15 10 ? * 6L 2002-2005" Fire at 10:15am on every last Friday of every
* month during the years 2002, 2003, 2004 and 2005<br>
*
* "0 15 10 ? * 6#3" Fire at 10:15am on the third Friday of every month<br>
*
* <br/><br/>created on 11.10.2005
*
*/
public class SchedulingService {
/**
*
*/
public static final String CRAWL_JOB = "crawl";
private Scheduler fScheduler;
private static final Log LOG = LogFactory.getLog(SchedulingService.class);
/**
* @throws SchedulerException
* @throws IOException
*/
public SchedulingService(PathSerializable fileStorePath)
throws SchedulerException, IOException {
InputStream resourceAsStream =
SchedulingService.class.getResourceAsStream("/quartz.properties");
Properties properties = new Properties();
if (resourceAsStream != null) {
// load values from config file
properties.load(resourceAsStream);
} else {
// use some default values
properties.put(
StdSchedulerFactory.PROP_JOB_STORE_CLASS,
FileJobStore.class.getName()
);
properties.put(
StdSchedulerFactory.PROP_THREAD_POOL_CLASS,
"org.quartz.simpl.SimpleThreadPool"
);
properties.put("org.quartz.threadPool.threadCount", "5");
}
properties.setProperty(
"org.quartz.jobStore.storeFilePath",
fileStorePath.toString()
);
SchedulerFactory schedulerFactory = new StdSchedulerFactory(properties);
this.fScheduler = schedulerFactory.getScheduler();
this.fScheduler.start();
}
/**
* @throws SchedulerException
*/
public void shutdown() throws SchedulerException {
this.fScheduler.shutdown();
}
/**
* There will be one trigger for job, with the same name and group as the job.
* All optional parameters could be left blank (null). For
* cron-expression-examples see class description.
*
* @param jobName
* @param jobGroup
* (optional)
* @param jobClass
* @param jobData
* (optional), will be available in
* Job#execute(org.quartz.JobExecutionContext)
* @param cronExpression
* @throws ParseException
* @throws SchedulerException
*/
public void scheduleCronJob(
String jobName, String jobGroup, Class jobClass, Map<String,PathSerializable> jobData,
String cronExpression) throws ParseException, SchedulerException {
JobDetail jobDetail = new JobDetail(jobName, jobGroup, jobClass);
if (jobData != null) {
jobDetail.setJobDataMap(new JobDataMap(jobData));
}
CronTrigger trigger = new CronTrigger(jobName, jobGroup);
trigger.setCronExpression(cronExpression);
this.fScheduler.deleteJob(jobName, jobGroup);
this.fScheduler.scheduleJob(jobDetail, trigger);
LOG.info("Job scheduled: jobName: " + jobName + " cronExp: " + cronExpression );
}
/**
* Schedule job with striped cronExpression. The cron expression will be
* integrated from the single parameters sec - year. If you leave those
* parameter blank, the "*"-character will be inserted. For
* cron-expression-examples see class description.
*
* There will be one trigger for job, with the same name and group as the job.
*
* All optional parameters could be left blank (null).
*
* @param jobName
* @param jobGroup
* (optional)
* @param jobClass
* @param jobData
* (optional), will be available in
* Job#execute(org.quartz.JobExecutionContext)
* @param sec
* @param min
* @param hours
* @param dayOfMonth
* @param month
* @param dayOfWeek
* @param year
* @throws SchedulerException
* @throws ParseException
*/
public void scheduleCronJob(
String jobName, String jobGroup, Class jobClass, Map jobData, String sec,
String min, String hours, String dayOfMonth, String month,
String dayOfWeek, String year) throws ParseException, SchedulerException {
String cronExpression =
getCronExpression(sec, min, hours, dayOfMonth, month, dayOfWeek, year);
scheduleCronJob(jobName, jobGroup, jobClass, jobData, cronExpression);
LOG.info("Job scheduled: jobName: " + jobName + " time: " + year + " " + month + " " + dayOfMonth + " "
+ hours + " " + min + " " + sec);
}
/**
* @param jobName
* @param groupName
* @return true if job exists
* @throws SchedulerException
*/
public boolean removeJob(String jobName, String groupName)
throws SchedulerException {
return this.fScheduler.deleteJob(jobName, groupName);
}
/**
* The cron expression will be integrated from the single parameters sec -
* year. If you leave those parameter blank, the "*"-character will be
* inserted. For cron-expression-examples see class description.
*
* @param sec
* @param min
* @param hours
* @param dayOfMonth
* @param month
* @param dayOfWeek
* @param year
* @return the integrated cron expression
*/
public static String getCronExpression(
String sec, String min, String hours, String dayOfMonth, String month,
String dayOfWeek, String year) {
StringBuffer buffer = new StringBuffer();
appendNextExpression(buffer, sec);
appendNextExpression(buffer, min);
appendNextExpression(buffer, hours);
appendNextExpression(buffer, dayOfMonth);
appendNextExpression(buffer, month);
appendNextExpression(buffer, dayOfWeek);
appendNextExpression(buffer, year);
return buffer.toString();
}
private static void appendNextExpression(
StringBuffer buffer, String expression) {
if (buffer.length() > 0) {
buffer.append(" ");
}
if (expression == null) {
buffer.append("*");
} else {
buffer.append(expression);
}
}
/**
* @return
* @throws SchedulerException
*/
public String getCronExpressions(String jobName, String jobGroup)
throws SchedulerException {
String ret = null;
Trigger trigger = this.fScheduler.getTrigger(jobName, jobGroup);
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
ret = cronTrigger.getCronExpression();
}
return ret;
}
/**
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void deleteJob(String jobName, String jobGroup)
throws SchedulerException {
this.fScheduler.deleteJob(jobName, jobGroup);
}
}