KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > oddjob > jobs > AbstractJob


1 package org.oddjob.jobs;
2
3
4 import java.beans.PropertyChangeListener JavaDoc;
5 import java.beans.PropertyChangeSupport JavaDoc;
6 import java.io.IOException JavaDoc;
7 import java.io.ObjectInputStream JavaDoc;
8 import java.io.ObjectOutputStream JavaDoc;
9 import java.io.Serializable JavaDoc;
10
11 import org.apache.log4j.Logger;
12 import org.apache.log4j.MDC;
13 import org.oddjob.Iconic;
14 import org.oddjob.Resetable;
15 import org.oddjob.Stateful;
16 import org.oddjob.arooa.ArooaContext;
17 import org.oddjob.arooa.ArooaConstants;
18 import org.oddjob.arooa.Lifecycle;
19 import org.oddjob.arooa.Location;
20 import org.oddjob.arooa.RuntimeConfiguration;
21 import org.oddjob.images.IconHelper;
22 import org.oddjob.images.IconListener;
23 import org.oddjob.images.IconTip;
24 import org.oddjob.logging.Log4jArchiver;
25 import org.oddjob.state.JobState;
26 import org.oddjob.state.JobStateEvent;
27 import org.oddjob.state.JobStateHandler;
28 import org.oddjob.state.JobStateListener;
29 import org.oddjob.state.StateTransform;
30 import org.oddjob.structural.StatefulChildHelper;
31 import org.oddjob.structural.StructuralListener;
32 import org.oddjob.util.Lock;
33 import org.oddjob.util.OddjobLockedException;
34 import org.xml.sax.Locator JavaDoc;
35
36 /**
37  * An abstract implementation of a job which provides commen functionality to
38  * concrete sub classes.
39  *
40  * @author Rob Gordon
41  */

42
43 public abstract class AbstractJob
44         implements Serializable JavaDoc,
45             Runnable JavaDoc, Resetable, Stateful, Iconic {
46     private static final long serialVersionUID = 20041005;
47
48     /** provides a unique logger per component. */
49     private static int instanceCount;
50
51     /** The logger. */
52     private transient String JavaDoc logger;
53     private transient Logger theLogger;
54     
55     /** The location. */
56     private transient Location location;
57     
58     /** RuntimeConfigurable. */
59     private transient RuntimeConfiguration wrapper;
60     
61     /**
62      * Implement property change support which sub classes can take advantage of.
63      */

64     protected transient PropertyChangeSupport JavaDoc changes;
65
66     /**
67      * Used to notify clients of an icon change.
68      */

69     protected IconHelper iconHelper = new IconHelper(this);
70     
71     /**
72      * Intended to be used by decendent classes to
73      * help implement Structural.
74      */

75     protected transient StatefulChildHelper childHelper;
76             
77     /**
78      * A state handler to delagate state change functionality to.
79      * */

80     protected JobStateHandler stateHandler = new JobStateHandler(this);
81
82     /**
83      * @oddojb.property
84      * @oddjob.description The name of the job.
85      * @oddjob.required No.
86      */

87     private String JavaDoc name;
88
89     /**
90      * This flag is set by the stop method and should
91      * be examined by any Stoppable sub classes in
92      * their processing loop.
93      */

94     protected transient volatile boolean stop;
95     
96     /**
97      * This flag is set once the object is destroyed
98      * Methods in subclass should check this flag.
99      */

100     protected transient volatile boolean destroyed;
101     
102     /**
103      * This locks the component. It can be used
104      * by methods in subclasses to ensure the component
105      * is locked before proceeding.
106      */

107     protected transient Lock lock;
108     
109     public AbstractJob() {
110         completeConstruction();
111     }
112     
113     /**
114      * Complete construction after constructor or deserialisation.
115      *
116      */

117     private void completeConstruction() {
118         changes = new PropertyChangeSupport JavaDoc(this);
119         childHelper = new StatefulChildHelper(this);
120         lock = new Lock();
121     }
122     
123     /**
124      * Allow sub classes access to the logger.
125      *
126      * @return The logger.
127      */

128     public Logger logger() {
129         if (theLogger == null) {
130             int count = 0;
131             synchronized (AbstractJob.class) {
132                 count = instanceCount++;
133             }
134             theLogger = Logger.getLogger(this.getClass().getName()
135                     + "." + count);
136         }
137         return theLogger;
138     }
139         
140     public void setContext(ArooaContext context) {
141         Locator JavaDoc locator = context.getLocator();
142         if (locator != null) {
143             Location location = new Location(locator.getSystemId(),
144                     locator.getLineNumber(),
145                     locator.getColumnNumber());
146             this.location = location;
147         }
148         this.wrapper = (RuntimeConfiguration) context.get(ArooaConstants.CURRENTLY_CONFIGURING);
149     }
150     
151     public Location getLocation() {
152         return this.location;
153     }
154     
155     protected RuntimeConfiguration getRuntimeConfigurable() {
156         return wrapper;
157     }
158     
159     /**
160      * Utility method to set the job state to exception.
161      *
162      * @param err An error message.
163      * @param ex The exception.
164      */

165     protected void setJobStateException(Throwable JavaDoc ex) {
166         stateHandler.setJobStateException(ex);
167         iconHelper.changeIcon(IconHelper.EXCEPTION);
168     }
169
170     /**
171      * Utility method to sleep a certain time.
172      *
173      * @param waitTime Milliseconds to sleep for.
174      */

175     protected void sleep(long waitTime) {
176         if (destroyed) {
177             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
178         }
179         iconHelper.changeIcon(IconHelper.SLEEPING);
180         logger().debug("[" + this + "] Sleeping for [" + waitTime + "] milli seconds." );
181         try {
182             synchronized (this) {
183                 wait(waitTime);
184             }
185         } catch (InterruptedException JavaDoc e) {
186             logger().debug("Sleep interupted.");
187         }
188         iconHelper.changeIcon(IconHelper.EXECUTING);
189     }
190     
191     /**
192      * Allow subclasses to indicate they are
193      * stopping. The subclass must still implement
194      * Stoppable.
195      */

196     public void stop() {
197         if (destroyed) {
198             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
199         }
200         if (stateHandler.getJobState() != JobState.EXECUTING) {
201             return;
202         }
203         iconHelper.changeIcon(IconHelper.STOPPING);
204         childHelper.stopChildren();
205         stop = true;
206         synchronized (this) {
207             notifyAll();
208         }
209         logger().debug("Thread [" + Thread.currentThread().getName()
210                 + "] requested [" + getName() + "] stop.");
211     }
212     
213     /**
214      * Execute this job.
215      *
216      * @return 0 if the job is complete, anything else otherwise.
217      * @throws Exception If the unexpected occurs.
218      */

219     abstract protected int execute() throws Throwable JavaDoc;
220
221     /**
222      * Implement the main execute method for a job. This surrounds the
223      * doExecute method of the sub class and sets state for the job.
224      */

225     public final void run() throws OddjobLockedException {
226         lock.accquire("Job executing.");
227         String JavaDoc oldMDC = (String JavaDoc)MDC.get(Log4jArchiver.MDC);
228         try {
229             MDC.put(Log4jArchiver.MDC, getLogger());
230             if (!stateHandler.requestJobStateExecuting()) {
231                 logger().debug("Can't execute job [" + this + "] because state is ["
232                         + stateHandler.getJobState() + "]");
233                 return;
234             }
235             iconHelper.changeIcon(IconHelper.EXECUTING);
236     
237             // runtime configuration.
238
if (wrapper != null) {
239                 try {
240                     logger().debug("Configuring job [" + this + "]");
241                     wrapper.configure();
242                 } catch (Exception JavaDoc e) {
243                     logger().error("Configruation exception for job: "
244                         + this + ", location: " + getLocation(), e);
245                     setJobStateException(e);
246                     return;
247                 }
248             }
249             logger().debug("Executing job [" + this + "]");
250         
251             int result = -1;
252             try {
253                 result = execute();
254             }
255             catch (Throwable JavaDoc e) {
256                 logger().info("Job Exception in [" + this + "]", e);
257                 setJobStateException(e);
258                 return;
259             }
260     
261             logger().debug("Finished job [" + this + "], result " + result);
262         
263             if (result == 0) {
264                 iconHelper.changeIcon(IconHelper.COMPLETE);
265                 stateHandler.setJobStateComplete();
266             }
267             else {
268                 iconHelper.changeIcon(IconHelper.NOT_COMPLETE);
269                 stateHandler.setJobStateNotComplete();
270             }
271         }
272         finally {
273             if (oldMDC != null) {
274                 MDC.put(Log4jArchiver.MDC, oldMDC);
275             }
276             else {
277                 MDC.remove(Log4jArchiver.MDC);
278             }
279             lock.release();
280         }
281     }
282     
283     /**
284      * Set the job name. Used by subclasses to set the job name.
285      *
286      * @param name The name of the job.
287      */

288     synchronized public void setName(String JavaDoc name) {
289         if (destroyed) {
290             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
291         }
292         String JavaDoc old = this.name;
293         this.name = name;
294         changes.firePropertyChange("name", old, name);
295     }
296
297     /**
298      * Get the job name.
299      *
300      * @return The job name.
301      */

302     synchronized public String JavaDoc getName() {
303         if (name == null && wrapper != null) {
304             return wrapper.getAttribute("name");
305         }
306         return name;
307     }
308     
309     /**
310      * Returns the last JobState event. This is useful when Jobs are being
311      * used directly in code, and only one thread is using the job. Otherwise
312      * a JobStateListener should always be used.
313      * <p>
314      * This is not a property so that it can't be accessed directly in scripts.
315      *
316      * @return The last JobStateEvent. Will never bu null.
317      */

318     
319     public JobStateEvent lastJobStateEvent() {
320         return stateHandler.getLastEvent();
321     }
322     
323     /**
324      * Add a job state listener.
325      */

326     
327     public void addJobStateListener(JobStateListener listener) {
328         if (destroyed) {
329             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
330         }
331         stateHandler.addJobStateListener(listener);
332     }
333
334     /**
335      * Remove a job state listener.
336      */

337     
338     public void removeJobStateListener(JobStateListener listener){
339         stateHandler.removeJobStateListener(listener);
340     }
341     
342     /**
343      * Add a property change listener.
344      *
345      * @param l The property change listener.
346      */

347     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
348         if (destroyed) {
349             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
350         }
351         changes.addPropertyChangeListener(l);
352     }
353
354     /**
355      * Remove a property change listener.
356      *
357      * @param l The property change listener.
358      */

359     public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
360         changes.removePropertyChangeListener(l);
361     }
362
363     /**
364      * Used by subclassses to perform a reset.
365      *
366      * @param transform A state transform.
367      * @return true if it succeeds, false otherwise.
368      */

369     protected boolean reset(StateTransform transform) {
370
371         if (transform.canReset(stateHandler.getJobState())) {
372             iconHelper.changeIcon(IconHelper.READY);
373             stop = false;
374             stateHandler.setJobStateReady();
375             return true;
376         }
377         else {
378             return false;
379         }
380     }
381
382     /**
383      * Perform a soft reset on the job.
384      */

385     public void softReset() {
386         lock.accquire("Soft reset in progress.");
387         try {
388             logger().debug("Thread [" + Thread.currentThread().getName()
389                     + "] soft reset for [" + getName() + "].");
390             childHelper.softResetChildren();
391             reset(StateTransform.SOFT_RESET);
392         }
393         finally {
394             lock.release();
395         }
396     }
397     
398     /**
399      * Perform a hard reset on the job.
400      */

401     public void hardReset() {
402         lock.accquire("Hard reset in progress.");
403         try {
404             logger().debug("Thread [" + Thread.currentThread().getName()
405                     + "] hard reset for [" + getName() + "].");
406             childHelper.hardResetChildren();
407             reset(StateTransform.HARD_RESET);
408         }
409         finally {
410             lock.release();
411         }
412     }
413
414     /**
415      * Return an icon tip for a given id. Part
416      * of the Iconic interface.
417      */

418     public IconTip iconForId(String JavaDoc iconId) {
419         return iconHelper.iconForId(iconId);
420     }
421
422     /**
423      * Add an icon listener. Part of the Iconic
424      * interface.
425      *
426      * @param listener The listener.
427      */

428     public void addIconListener(IconListener listener) {
429         if (destroyed) {
430             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
431         }
432         iconHelper.addIconListener(listener);
433     }
434
435     /**
436      * Remove an icon listener. Part of the Iconic
437      * interface.
438      *
439      * @param listener The listener.
440      */

441     public void removeIconListener(IconListener listener) {
442         iconHelper.removeIconListener(listener);
443     }
444
445     /**
446      * Add a listener. The listener will immediately recieve add
447      * notifications for all existing children.
448      *
449      * @param listener The listener.
450      */

451     public void addStructuralListener(StructuralListener listener) {
452         if (destroyed) {
453             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
454         }
455         childHelper.addStructuralListener(listener);
456     }
457     
458     /**
459      * Remove a listener.
460      *
461      * @param listener The listner.
462      */

463     public void removeStructuralListener(StructuralListener listener) {
464         childHelper.removeStructuralListener(listener);
465     }
466     
467     /**
468      * Override toString.
469      */

470     public String JavaDoc toString() {
471         if (name == null) {
472             return getClass().getName();
473         }
474         else {
475             return name;
476         }
477     }
478
479     /**
480      * Destroy this component.
481      */

482     public void destroy() {
483         if (destroyed) {
484             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
485         }
486         Lifecycle.destroy(childHelper.getChildren());
487         childHelper.removeAllChildren();
488         destroyed = true;
489         logger().debug("Destroyed [" + this + "]");
490     }
491     
492     /**
493      * Custom serialsation.
494      */

495     private void writeObject(ObjectOutputStream JavaDoc s)
496     throws IOException JavaDoc {
497         if (destroyed) {
498             throw new IllegalStateException JavaDoc("[" + this + "] destroyed");
499         }
500         s.defaultWriteObject();
501     }
502
503     /**
504      * Custom serialisation.
505      */

506     private void readObject(ObjectInputStream JavaDoc s)
507     throws IOException JavaDoc, ClassNotFoundException JavaDoc {
508         s.defaultReadObject();
509         stateHandler.setSource(this);
510         iconHelper.setSource(this);
511         completeConstruction();
512     }
513
514     /**
515      * @return Returns the logger.
516      */

517     public String JavaDoc getLogger() {
518         return logger().getName();
519     }
520     
521     /**
522      * @param logger The logger to set.
523      */

524     public void setLogger(String JavaDoc logger) {
525         this.logger = logger;
526         if (theLogger != null) {
527             theLogger.debug("Logger being replaced by [" + logger + "]");
528         }
529         theLogger = Logger.getLogger(logger);
530     }
531 }
532
Popular Tags