| Method from org.quartz.xml.JobSchedulingDataProcessor Detail: |
public void addCalendar(Scheduler sched,
CalendarBundle calendarBundle) throws SchedulerException {
sched.addCalendar(
calendarBundle.getCalendarName(),
calendarBundle.getCalendar(),
calendarBundle.getReplace(),
true);
}
|
public void addCalendarToSchedule(CalendarBundle cal) {
calsToSchedule.add(cal);
}
|
protected boolean addCustomDigesterRules(Digester digester) {
// do nothing in base impl
return true;
}
Template method provided as a hook for those who wish to extend this
class and add more functionality.
This method is invoked after the Digester is instantiated, and before
the default set of rules are added. |
protected void addDefaultDigesterRules(Digester digester) {
digester.addSetProperties(TAG_QUARTZ, TAG_OVERWRITE_EXISTING_JOBS, "overWriteExistingJobs");
digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB_LISTENER, "jobListener","class-name");
digester.addCallMethod(TAG_QUARTZ + "/" + TAG_JOB_LISTENER,"setName",1);
digester.addCallParam(TAG_QUARTZ + "/" + TAG_JOB_LISTENER,0,"name");
digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB_LISTENER,"addListenerToSchedule");
digester.addRuleSet(new CalendarRuleSet(TAG_QUARTZ + "/" + TAG_CALENDAR, "addCalendarToSchedule"));
digester.addRuleSet(new CalendarRuleSet("*/" + TAG_BASE_CALENDAR, "setBaseCalendar"));
digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB, JobSchedulingBundle.class);
digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL, JobDetail.class);
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_NAME, "name");
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_GROUP, "group");
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_DESCRIPTION, "description");
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_CLASS, "jobClass");
digester.addCallMethod(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_LISTENER_REF,"addJobListener",0 );
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_VOLATILITY, "volatility");
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_DURABILITY, "durability");
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_RECOVER, "requestsRecovery");
digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP, JobDataMap.class);
digester.addCallMethod(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY, "put", 2, new Class[] { Object.class, Object.class });
digester.addCallParam(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY + "/" + TAG_KEY, 0);
digester.addCallParam(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY + "/" + TAG_VALUE, 1);
digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP, "setJobDataMap");
digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL, "setJobDetail");
digester.addRuleSet(new TriggerRuleSet(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE, SimpleTrigger.class));
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE + "/" + TAG_REPEAT_COUNT, "repeatCount");
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE + "/" + TAG_REPEAT_INTERVAL, "repeatInterval");
digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE, "addTrigger");
digester.addRuleSet(new TriggerRuleSet(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON, CronTrigger.class));
digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON + "/" + TAG_CRON_EXPRESSION, "cronExpression");
digester.addRule(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON + "/" + TAG_TIME_ZONE, new SimpleConverterRule("timeZone", new TimeZoneConverter(), TimeZone.class));
digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON, "addTrigger");
digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB, "addJobToSchedule");
}
Add the default set of digest rules |
public void addJobToSchedule(JobSchedulingBundle job) {
jobsToSchedule.add(job);
}
|
public void addListenerToSchedule(JobListener listener) {
listenersToSchedule.add(listener);
}
|
protected void addScheduledJob(JobSchedulingBundle job) {
scheduledJobs.put(job.getFullName(), job);
}
|
protected void addValidationException(SAXException e) {
validationExceptions.add(e);
}
Adds a detected validation exception. |
protected void clearValidationExceptions() {
validationExceptions.clear();
}
Resets the the number of detected validation exceptions. |
public void error(SAXParseException e) throws SAXException {
addValidationException(e);
}
ErrorHandler interface.
Receive notification of a recoverable error. |
public void fatalError(SAXParseException e) throws SAXException {
addValidationException(e);
}
ErrorHandler interface.
Receive notification of a non-recoverable error. |
protected InputStream getInputStream(String fileName) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputStream is = cl.getResourceAsStream(fileName);
return is;
}
Returns an InputStream from the fileName as a resource. |
protected Log getLog() {
return log;
}
|
public boolean getOverWriteExistingJobs() {
return overWriteExistingJobs;
}
Returns whether to overwrite existing jobs. |
public JobSchedulingBundle getScheduledJob(String name) {
return (JobSchedulingBundle) getScheduledJobs().get(name);
}
Returns a JobSchedulingBundle for the job name. |
public Map getScheduledJobs() {
return Collections.unmodifiableMap(scheduledJobs);
}
Returns a Map of scheduled jobs.
The key is the job name and the value is a JobSchedulingBundle
containing the JobDetail and Trigger. |
protected String getSystemIdForFileName(String fileName) {
InputStream fileInputStream = null;
try {
String urlPath = null;
File file = new File(fileName); // files in filesystem
if (!file.exists()) {
URL url = getURL(fileName);
if (url != null) {
// Required for jdk 1.3 compatibility
urlPath = URLDecoder.decode(url.getPath());
try {
fileInputStream = url.openStream();
} catch (IOException ignore) {
}
}
} else {
try {
fileInputStream = new FileInputStream(file);
}catch (FileNotFoundException ignore) {
}
}
if (fileInputStream == null) {
getLog().debug("Unable to resolve '" + fileName + "' to full path, so using it as is for system id.");
return fileName;
} else {
return (urlPath != null) ? urlPath : file.getAbsolutePath();
}
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException ioe) {
getLog().warn("Error closing jobs file: " + fileName, ioe);
}
}
}
For the given fileName, attempt to expand it to its full path
for use as a system id. |
protected URL getURL(String fileName) {
return Thread.currentThread().getContextClassLoader().getResource(fileName);
}
Returns an URL from the fileName as a resource. |
public boolean getUseContextClassLoader() {
return digester.getUseContextClassLoader();
}
Returns whether to use the context class loader. |
protected void initDigester(boolean useContextClassLoader,
boolean validating,
boolean validatingSchema) {
digester = new Digester();
digester.setNamespaceAware(true);
digester.setUseContextClassLoader(useContextClassLoader);
digester.setValidating(validating);
initSchemaValidation(validatingSchema);
digester.setEntityResolver(this);
digester.setErrorHandler(this);
if (addCustomDigesterRules(digester)) {
addDefaultDigesterRules(digester);
}
}
Initializes the digester. |
protected void initSchemaValidation(boolean validatingSchema) {
if (validatingSchema) {
String schemaUri = null;
URL url = getClass().getResource(QUARTZ_XSD);
if (url != null) {
schemaUri = url.toExternalForm();
} else {
schemaUri = QUARTZ_SCHEMA;
}
digester.setSchema(schemaUri);
}
}
Initializes the digester for XML Schema validation. |
protected void maybeThrowValidationException() throws ValidationException {
if (validationExceptions.size() > 0) {
throw new ValidationException(validationExceptions);
}
}
Throws a ValidationException if the number of validationExceptions
detected is greater than zero. |
public void processFile() throws Exception {
processFile(QUARTZ_XML_FILE_NAME);
}
Process the xml file in the default location (a file named
"quartz_jobs.xml" in the current working directory). |
public void processFile(String fileName) throws Exception {
processFile(fileName, getSystemIdForFileName(fileName));
}
Process the xml file named fileName. |
public void processFile(String fileName,
String systemId) throws SchedulerException, ClassNotFoundException, IOException, ValidationException, SAXException, ParseException, ParserConfigurationException {
clearValidationExceptions();
scheduledJobs.clear();
jobsToSchedule.clear();
calsToSchedule.clear();
getLog().info("Parsing XML file: " + fileName +
" with systemId: " + systemId +
" validating: " + digester.getValidating() +
" validating schema: " + digester.getSchema());
InputSource is = new InputSource(getInputStream(fileName));
is.setSystemId(systemId);
digester.push(this);
digester.parse(is);
maybeThrowValidationException();
}
Process the xmlfile named fileName with the given system
ID. |
public void processFileAndScheduleJobs(Scheduler sched,
boolean overWriteExistingJobs) throws Exception, SchedulerException {
processFileAndScheduleJobs(QUARTZ_XML_FILE_NAME, sched,
overWriteExistingJobs);
}
Process the xml file in the default location, and schedule all of the
jobs defined within it. |
public void processFileAndScheduleJobs(String fileName,
Scheduler sched,
boolean overWriteExistingJobs) throws Exception {
processFileAndScheduleJobs(fileName, getSystemIdForFileName(fileName), sched, overWriteExistingJobs);
}
Process the xml file in the given location, and schedule all of the
jobs defined within it. |
public void processFileAndScheduleJobs(String fileName,
String systemId,
Scheduler sched,
boolean overWriteExistingJobs) throws Exception {
schedLocal.set(sched);
try {
processFile(fileName, systemId);
scheduleJobs(getScheduledJobs(), sched, overWriteExistingJobs);
} finally {
schedLocal.set(null);
}
}
Process the xml file in the given location, and schedule all of the
jobs defined within it. |
public void processStream(InputStream stream,
String systemId) throws SchedulerException, ClassNotFoundException, IOException, ValidationException, SAXException, ParseException, ParserConfigurationException {
clearValidationExceptions();
scheduledJobs.clear();
jobsToSchedule.clear();
calsToSchedule.clear();
getLog().info("Parsing XML from stream with systemId: " + systemId +
" validating: " + digester.getValidating() +
" validating schema: " + digester.getSchema());
InputSource is = new InputSource(stream);
is.setSystemId(systemId);
digester.push(this);
digester.parse(is);
maybeThrowValidationException();
}
Process the xmlfile named fileName with the given system
ID. |
public InputSource resolveEntity(String publicId,
String systemId) {
InputSource inputSource = null;
InputStream is = null;
URL url = null;
try {
if (publicId == null) {
if (systemId != null) {
// resolve Quartz Schema locally
if (QUARTZ_SCHEMA.equals(systemId)) {
is = getClass().getResourceAsStream(QUARTZ_XSD);
} else {
is = getInputStream(systemId);
if (is == null) {
int start = systemId.indexOf(QUARTZ_SYSTEM_ID_PREFIX);
if (start > -1) {
String fileName = systemId
.substring(QUARTZ_SYSTEM_ID_PREFIX.length());
is = getInputStream(fileName);
} else {
if (systemId.indexOf(':") == -1) {
File file = new java.io.File(systemId);
url = file.toURL();
} else {
url = new URL(systemId);
}
is = url.openStream();
}
}
}
}
} else {
// resolve Quartz DTD locally
if (QUARTZ_PUBLIC_ID.equals(publicId)) {
is = getClass().getResourceAsStream(QUARTZ_DTD);
} else {
url = new URL(systemId);
is = url.openStream();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
inputSource = new InputSource(is);
inputSource.setPublicId(publicId);
inputSource.setSystemId(systemId);
}
}
return inputSource;
}
EntityResolver interface.
Allow the application to resolve external entities.
Until quartz.dtd has a public ID, it must resolved as a
system ID. Here's the order of resolution (if one fails, continue to the
next).
- Tries to resolve the
systemId with ClassLoader.getResourceAsStream(String).
- If the
systemId starts with QUARTZ_SYSTEM_ID_PREFIX,
then resolve the part after QUARTZ_SYSTEM_ID_PREFIX with
ClassLoader.getResourceAsStream(String).
- Else try to resolve
systemId as a URL.
- If
systemId has a colon in it, create a new URL
- Else resolve
systemId as a File and
then call File.toURL().
If the publicId does exist, resolve it as a URL. If the
publicId is the Quartz public ID, then resolve it locally. |
public void scheduleJob(JobSchedulingBundle job) throws SchedulerException {
scheduleJob(job, (Scheduler) schedLocal.get(), getOverWriteExistingJobs());
}
Schedules a given job and trigger (both wrapped by a JobSchedulingBundle). |
public void scheduleJob(JobSchedulingBundle job,
Scheduler sched,
boolean localOverWriteExistingJobs) throws SchedulerException {
if ((job != null) && job.isValid()) {
JobDetail detail = job.getJobDetail();
JobDetail dupeJ = sched.getJobDetail(detail.getName(), detail.getGroup());
if ((dupeJ != null) && !localOverWriteExistingJobs) {
getLog().info("Not overwriting existing job: " + dupeJ.getFullName());
return;
}
if (dupeJ != null) {
getLog().info("Replacing job: " + detail.getFullName());
} else {
getLog().info("Adding job: " + detail.getFullName());
}
if (job.getTriggers().size() == 0 && !job.getJobDetail().isDurable()) {
if (dupeJ == null) {
throw new SchedulerException(
"A new job defined without any triggers must be durable: " +
detail.getFullName());
}
if ((dupeJ.isDurable() &&
(sched.getTriggersOfJob(
detail.getName(), detail.getGroup()).length == 0))) {
throw new SchedulerException(
"Can't make a durable job without triggers non-durable: " +
detail.getFullName());
}
}
sched.addJob(detail, true);
for (Iterator iter = job.getTriggers().iterator(); iter.hasNext(); ) {
Trigger trigger = (Trigger)iter.next();
trigger.setJobName(detail.getName());
trigger.setJobGroup(detail.getGroup());
if(trigger.getStartTime() == null) {
trigger.setStartTime(new Date());
}
boolean addedTrigger = false;
while (addedTrigger == false) {
Trigger dupeT = sched.getTrigger(trigger.getName(), trigger.getGroup());
if (dupeT != null) {
if (getLog().isDebugEnabled()) {
getLog().debug(
"Rescheduling job: " + detail.getFullName() + " with updated trigger: " + trigger.getFullName());
}
if(!dupeT.getJobGroup().equals(trigger.getJobGroup()) || !dupeT.getJobName().equals(trigger.getJobName())) {
getLog().warn("Possibly duplicately named triggers in jobs xml file!");
}
sched.rescheduleJob(trigger.getName(), trigger.getGroup(), trigger);
} else {
if (getLog().isDebugEnabled()) {
getLog().debug(
"Scheduling job: " + detail.getFullName() + " with trigger: " + trigger.getFullName());
}
try {
sched.scheduleJob(trigger);
} catch (ObjectAlreadyExistsException e) {
if (getLog().isDebugEnabled()) {
getLog().debug(
"Adding trigger: " + trigger.getFullName() + " for job: " + detail.getFullName() +
" failed because the trigger already existed. " +
"This is likely due to a race condition between multiple instances " +
"in the cluster. Will try to reschedule instead.");
}
continue;
}
}
addedTrigger = true;
}
}
addScheduledJob(job);
}
}
Schedules a given job and trigger (both wrapped by a JobSchedulingBundle). |
public void scheduleJobs(Map jobBundles,
Scheduler sched,
boolean overWriteExistingJobs) throws Exception {
getLog().info("Scheduling " + jobsToSchedule.size() + " parsed jobs.");
Iterator itr = calsToSchedule.iterator();
while (itr.hasNext()) {
CalendarBundle bndle = (CalendarBundle) itr.next();
addCalendar(sched, bndle);
}
itr = jobsToSchedule.iterator();
while (itr.hasNext()) {
JobSchedulingBundle bndle = (JobSchedulingBundle) itr.next();
scheduleJob(bndle, sched, overWriteExistingJobs);
}
itr = listenersToSchedule.iterator();
while (itr.hasNext()) {
JobListener listener = (JobListener) itr.next();
getLog().info("adding listener "+listener.getName()+" of class "+listener.getClass().getName());
sched.addJobListener(listener);
}
getLog().info(jobBundles.size() + " scheduled jobs.");
}
Add the Jobs and Triggers defined in the given map of JobSchedulingBundle
s to the given scheduler. |
public void setOverWriteExistingJobs(boolean overWriteExistingJobs) {
this.overWriteExistingJobs = overWriteExistingJobs;
}
Sets whether to overwrite existing jobs. |
public void setUseContextClassLoader(boolean useContextClassLoader) {
digester.setUseContextClassLoader(useContextClassLoader);
}
Sets whether to use the context class loader. |
public void warning(SAXParseException e) throws SAXException {
addValidationException(e);
}
ErrorHandler interface.
Receive notification of a warning. |