001: /*
002: * Copyright 2004-2006 OpenSymphony
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy
006: * of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations
014: * under the License.
015: */
016: package org.quartz.listeners;
017:
018: import java.util.HashMap;
019: import java.util.Map;
020:
021: import org.quartz.utils.Key;
022: import org.quartz.JobExecutionContext;
023: import org.quartz.JobExecutionException;
024: import org.quartz.SchedulerException;
025:
026: /**
027: * Keeps a collection of mappings of which Job to trigger after the completion
028: * of a given job. If this listener is notified of a job completing that has a
029: * mapping, then it will then attempt to trigger the follow-up job. This
030: * achieves "job chaining", or a "poor man's workflow".
031: *
032: * <p>Generally an instance of this listener would be registered as a global
033: * job listener, rather than being registered directly to a given job.</p>
034: *
035: * <p>If for some reason there is a failure creating the trigger for the
036: * follow-up job (which would generally only be caused by a rare serious
037: * failure in the system, or the non-existence of the follow-up job), an error
038: * messsage is logged, but no other action is taken. If you need more rigorous
039: * handling of the error, consider scheduling the triggering of the flow-up
040: * job within your job itself.</p>
041: *
042: * @author James House (jhouse AT revolition DOT net)
043: */
044: public class JobChainingJobListener extends JobListenerSupport {
045:
046: private String name;
047: private Map chainLinks;
048:
049: /**
050: * Construct an instance with the given name.
051: *
052: * @param name the name of this instance
053: */
054: public JobChainingJobListener(String name) {
055: if (name == null)
056: throw new IllegalArgumentException(
057: "Listener name cannot be null!");
058: this .name = name;
059: chainLinks = new HashMap();
060: }
061:
062: public String getName() {
063: return name;
064: }
065:
066: /**
067: * Add a chain mapping - when the Job identified by the first key completes
068: * the job identified by the second key will be triggered.
069: *
070: * @param firstJob a Key with the name and group of the first job
071: * @param secondJob a Key with the name and group of the follow-up job
072: */
073: public void addJobChainLink(Key firstJob, Key secondJob) {
074:
075: if (firstJob == null || secondJob == null)
076: throw new IllegalArgumentException("Key cannot be null!");
077: if (firstJob.getName() == null || secondJob.getName() == null)
078: throw new IllegalArgumentException(
079: "Key cannot have a null name!");
080:
081: chainLinks.put(firstJob, secondJob);
082: }
083:
084: public void jobWasExecuted(JobExecutionContext context,
085: JobExecutionException jobException) {
086:
087: Key sj = (Key) chainLinks.get(context.getJobDetail().getKey());
088:
089: if (sj == null)
090: return;
091:
092: getLog().info(
093: "Job '" + context.getJobDetail().getFullName()
094: + "' will now chain to Job '" + sj + "'");
095:
096: try {
097: if (context.getJobDetail().isVolatile()
098: || context.getTrigger().isVolatile())
099: context.getScheduler().triggerJobWithVolatileTrigger(
100: sj.getName(), sj.getGroup());
101: else
102: context.getScheduler().triggerJob(sj.getName(),
103: sj.getGroup());
104: } catch (SchedulerException se) {
105: getLog().error(
106: "Error encountered during chaining to Job '" + sj
107: + "'", se);
108: }
109: }
110: }
|