KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > oddjob > Oddjob


1 package org.oddjob;
2
3
4 import java.io.File JavaDoc;
5 import java.io.IOException JavaDoc;
6 import java.io.InputStream JavaDoc;
7 import java.io.ObjectInputStream JavaDoc;
8 import java.io.ObjectOutputStream JavaDoc;
9
10 import org.oddjob.arooa.ArooaClassLoader;
11 import org.oddjob.arooa.ArooaConstants;
12 import org.oddjob.arooa.ArooaContext;
13 import org.oddjob.arooa.ArooaFactory;
14 import org.oddjob.arooa.ArooaHandler;
15 import org.oddjob.arooa.ArooaRuntime;
16 import org.oddjob.arooa.Lifecycle;
17 import org.oddjob.arooa.Location;
18 import org.oddjob.arooa.ObjectFactory;
19 import org.oddjob.arooa.PropertyProxyResolver;
20 import org.oddjob.arooa.SimpleObjectFactory;
21 import org.oddjob.arooa.handlers.AttributeHelper;
22 import org.oddjob.arooa.handlers.DefaultComponentHandler;
23 import org.oddjob.arooa.handlers.SerializedComponentHandler;
24 import org.oddjob.arooa.reflect.IntrospectionHelper;
25 import org.oddjob.arooa.registry.ComponentRegistry;
26 import org.oddjob.arooa.registry.Path;
27 import org.oddjob.framework.BeanUtilsProvider;
28 import org.oddjob.framework.OddjobComponentResolver;
29 import org.oddjob.framework.StructuralJob;
30 import org.oddjob.logging.ConsoleArchive;
31 import org.oddjob.logging.ConsoleArchiveImpl;
32 import org.oddjob.logging.LogLevel;
33 import org.oddjob.logging.LoggingPrintStream;
34 import org.oddjob.persist.ComponentPersister;
35 import org.oddjob.util.OddjobConfigException;
36 import org.xml.sax.Attributes JavaDoc;
37 import org.xml.sax.SAXParseException JavaDoc;
38
39 /**
40  * Read a configuration, creates child jobs and
41  * executes them.
42  *
43  * @oddjob.description The starting point for a hierarchy of jobs. The Oddjob job
44  * creates and runs a job hierarchy by processing a supplied configuration file.
45  * <p>
46  * An Oddjob job allows an Oddjob instance to be created within an existing Oddjob
47  * configuration. This way complicated processes can be created in managable and
48  * separately testable units.
49  * <p>
50  * An Oddjob job can be visualised as having two sides. One side is in the
51  * configuration that creates the Oddjob. The other side is in the configuration
52  * that the Oddjob job creates and runs. An Oddjob can have two different ids - that
53  * which identifies it in the outer and that which identifies it in the
54  * inner configuration files.
55  * An Oddjob job should only have it's properties set in the outer
56  * configuration file. To enforce this the top level &lt;oddjob&gt; element
57  * only supports the id attribute.
58  * <p>
59  * The args property allows arguments to be passed into a nested Oddjob from the
60  * the outer oddjob or from the command line.
61  * <p>
62  * Properties of jobs in a nested Oddjob can be accessed using the notation
63  * <i>${nested-oddjob-id/job-id.property}</i> where nested-oddjob-id is the id in
64  * the outer configuration, not the inner one.
65  * <p>
66  * Oddjob has serveral properties to support registering bespoke jobs
67  * types, and property proxies. An example would be
68  * <pre>
69  * ...
70  * &lt;oddjob name="Nested Oddjob"
71  * file="nested.xml"&gt;
72  * &lt;componentType&gt;
73  * &lt;value name="myjob" value="com.mycomp.ProcessingJob"/&gt;
74  * &lt;/componentType&gt;
75  * &lt;/oddjob&gt;
76  * ...
77  * </pre>
78  * The file nested.xml can then contain something like.
79  * <pre>
80  * &lt;oddjob&gt;
81  * &lt;myjob/&gt;
82  * &lt;/oddjob&gt;
83  * </pre>
84  *
85  * The classpath that the nested Oddjob will use can also be configured.
86  *
87  * @oddjob.example
88  *
89  * A simple nested Oddjob with one argument.
90  * <pre>
91  * &lt;oddjob id="this"&gt;
92  * &lt;oddjob file="${this.dir}/nested.xml"&gt;
93  * &lt;args&gt;
94  * &lt;value value="apples"/&gt;
95  * &lt;/args&gt;
96  * &lt;/oddjob&gt;
97  * &ltl;/oddjob&gt;
98  * </pre>
99  *
100  * Oddjob with database persistance.
101  * <pre>
102  * &lt;oddjob file="${this.dir}\job.oddjob.persist.xml"
103  * name="A Database Persisted Oddjob"&gt;
104  * &lt;persister&gt;
105  * &lt;sqlPersister&gt;
106  * &lt;connection driver="org.hsqldb.jdbcDriver"
107  * url="jdbc:hsqldb:hsql://localhost:11002/ojdb" username="sa"/&gt;
108  * &lt;/sqlPersister&gt;
109  * &lt;/persister&gt;
110  * &lt;/oddjob&gt;
111  * </pre>
112  *
113  * @author Rob Gordon
114  */

115
116 public class Oddjob extends StructuralJob
117         implements Structural, Stoppable {
118     private static final long serialVersionUID = 20020506;
119     
120     public static final String JavaDoc JOB_PROPERTIES = "/org/oddjob/oddjob.properties";
121     public static final String JavaDoc CONVERTER_PROPERTIES = "/org/oddjob/converter.properties";
122     public static final String JavaDoc PROXY_PROPERTIES = "/org/oddjob/values/proxies.properties";
123     public static final String JavaDoc TYPE_PROPERTIES = "/org/oddjob/values/types.properties";
124     
125     public static final ConsoleArchive CONSOLE;
126     
127     static {
128         ConsoleArchiveImpl console = new ConsoleArchiveImpl();
129         System.setOut(new LoggingPrintStream(System.out, LogLevel.INFO,
130                 console.consoleLog()));
131         System.setErr(new LoggingPrintStream(System.err, LogLevel.ERROR,
132                 console.consoleLog()));
133         CONSOLE = console;
134     }
135      
136     /**
137      * @oddjob.property
138      * @oddjob.description The configuration file.
139      * @oddjob.required Yes.
140      */

141     private transient File JavaDoc file;
142         
143     /**
144      * @oddjob.property
145      * @oddjob.description A component which is able to save and restore
146      * jobs.
147      * @oddjob.required No.
148      */

149     private transient ComponentPersister persister;
150         
151
152     /**
153      * @oddjob.property
154      * @oddjob.description A value of STRICT will force Oddjob to check property
155      * substitutions for null values. Useful for debugging.
156      *
157      * @oddjob.required No.
158      */

159     private transient String JavaDoc substitution;
160
161     /**
162      * The component registry.
163      */

164     private transient ComponentRegistry componentRegistry;
165     private transient ComponentRegistry parentRegistry;
166     
167     /**
168      * @oddjob.property
169      * @oddjob.description An array of arguments the Oddjob configuration can use.
170      *
171      * @oddjob.required No.
172      */

173     private transient Object JavaDoc[] args;
174     
175     /** Optional input stream to read conifg from */
176     private transient InputStream JavaDoc input;
177     
178     /** Class loader */
179     private transient ClassLoader JavaDoc classLoader;
180
181     /**
182      * @oddjob.property
183      * @oddjob.description The classpath property can be used to
184      * specify where Oddjob looks for additional jar files. The
185      * classpath is an inline {@link org.oddjob.io.FilesType} property.
186      * @oddjob.required No.
187      */

188     private transient File JavaDoc[] classpath;
189     
190     /**
191      * @oddjob.property
192      * @oddjob.description If set Oddjob will only load the job definitions. It
193      * will not execute them. This
194      * is useful when writing Oddjob configuration files to check that they
195      * parse correctly.
196      * @oddjob.required No.
197      */

198     private transient boolean loadOnly;
199
200     /** The factory Oddjob will use to create components. */
201     private transient SimpleObjectFactory componentFactory;
202     
203     /** The factory Oddjob will use to create jobs. */
204     private transient SimpleObjectFactory valueFactory;
205     
206     /** The default type manager. */
207     private transient PropertyProxyResolver propertyProxyResolver;
208
209     /** The load context. preserving this allows Explorer to
210      * create values when setting component properties.
211      */

212     private ArooaContext loadContext;
213     
214     /**
215      * Set the configuration file.
216      *
217      * @param file The config file.
218      */

219     public void setFile(File JavaDoc file) {
220         this.file = file;
221     }
222
223     /**
224      * @oddjob.property file
225      * @oddjob.description The name of the configuration file.
226      * to configure this oddjob.
227      * @oddjob.required R/O
228      *
229      * @return The file name.
230      */

231     public File JavaDoc getFile() {
232         if (file == null) {
233             return null;
234         }
235         return file.getAbsoluteFile();
236     }
237     
238     /**
239      * @oddjob.property dir
240      * @oddjob.description The name of the directory the configuration
241      * file is in.
242      * @oddjob.required R/O
243      *
244      * @return The directory path.
245      */

246     public File JavaDoc getDir() {
247         if (file == null) {
248             return null;
249         }
250         return file.getAbsoluteFile().getParentFile();
251     }
252     
253     public void setConfig(File JavaDoc file) {
254         logger().warn("config is depricated use the file attribute instead.");
255         setFile(file);
256     }
257     
258     /**
259      * Set the input stream to read configuration from.
260      *
261      * @param inputStream The input stream.
262      */

263     public void setInput(InputStream JavaDoc inputStream) {
264         this.input = inputStream;
265     }
266     
267     /**
268      * Set a class loader.
269      *
270      * @param classLoader The classLoader;
271      */

272     public void setClassLoader(ClassLoader JavaDoc classLoader) {
273         this.classLoader = classLoader;
274     }
275     
276     /**
277      * Return a class loader. If one has not been set return
278      * the class loader which loaded this class.
279      *
280      * @return A classLoader.
281      */

282     public ClassLoader JavaDoc getClassLoader() {
283         return this.classLoader;
284     }
285
286     /**
287      * Set the classpath files.
288      *
289      * @param files The files to set in the classpath.
290      */

291     public void setClasspath(File JavaDoc[] files) {
292        this.classpath = files;
293     }
294         
295     /**
296      * Get the classpath files.
297      *
298      * @return The files in the classpath.
299      */

300     public void getClasspath(File JavaDoc[] files) {
301        this.classpath = files;
302     }
303     
304     /**
305      * @return Returns the substitution.
306      */

307     public String JavaDoc getSubstitution() {
308         return substitution;
309     }
310     /**
311      * @param substitution The substitution to set.
312      */

313     public void setSubstitution(String JavaDoc substitution) {
314         this.substitution = substitution.toUpperCase();
315     }
316     
317     /**
318      * @oddjob.property componentType
319      * @oddjob.description A mapped property for specifying
320      * additional component types.
321      * @oddjob.required No.
322      */

323     public void setComponentType(String JavaDoc name, String JavaDoc className) {
324         getComponentFactory().set(name, className);
325     }
326     
327     /**
328      * @oddjob.property valueType
329      * @oddjob.description A mapped property for specifying
330      * additional value types.
331      * @oddjob.required No.
332      */

333     public void setValueType(String JavaDoc name, String JavaDoc className) {
334         getValueFactory().set(name, className);
335     }
336     
337     /**
338      * @oddjob.property propertyProxy
339      * @oddjob.description A mapped property for specifying
340      * additional property proxies.
341      * @oddjob.required No.
342      */

343     public void setPropertyProxy(String JavaDoc name, String JavaDoc className) {
344         getPropertyProxyResolver().setProxy(name, className);
345     }
346     
347     /*
348      * (non-Javadoc)
349      * @see org.oddjob.jobs.AbstractJob#setContext(org.oddjob.arooa.ArooaXMLContext)
350      */

351     public boolean setContext(ArooaContext context) {
352         // set factories to chain parent factories so any defintion
353
// overridden in parent Oddjobs is picked up in nested ones.
354
ObjectFactory componentFactory = (ObjectFactory) context.get(ArooaConstants.COMPONENT_FACTORY);
355         if (componentFactory != null) {
356             this.componentFactory = new SimpleObjectFactory(componentFactory);
357         }
358         ObjectFactory valueFactory = (ObjectFactory) context.get(ArooaConstants.VALUE_FACTORY);
359         if (valueFactory != null) {
360             this.valueFactory = new SimpleObjectFactory(valueFactory);
361         }
362         PropertyProxyResolver propertyProxyResolver =
363             (PropertyProxyResolver) context.get(ArooaConstants.PROPERTY_PROXY_RESOLVER);
364         if (propertyProxyResolver != null) {
365             this.propertyProxyResolver = new PropertyProxyResolver(propertyProxyResolver);
366         }
367         // so we call link nested oddjobs.
368
parentRegistry = (ComponentRegistry) context.get(
369                 ArooaConstants.COMPONENT_REGISTRY);
370         return super.setContext(context);
371     }
372     
373     private SimpleObjectFactory getComponentFactory() {
374         if (componentFactory == null) {
375             componentFactory = new SimpleObjectFactory();
376             componentFactory.addResource(JOB_PROPERTIES);
377         }
378         return componentFactory;
379     }
380     
381     private SimpleObjectFactory getValueFactory() {
382         if (valueFactory == null) {
383             valueFactory = new SimpleObjectFactory();
384             valueFactory.addResource(TYPE_PROPERTIES);
385         }
386         return valueFactory;
387     }
388     
389     private PropertyProxyResolver getPropertyProxyResolver() {
390         if (propertyProxyResolver == null) {
391             propertyProxyResolver = new PropertyProxyResolver();
392             propertyProxyResolver.addResource(PROXY_PROPERTIES);
393         }
394         return propertyProxyResolver;
395     }
396     
397     /**
398      * Load Oddjob from the configuration file.
399      */

400     protected void load() {
401         lock.accquire("Oddjob loading.");
402         try {
403             logger().debug("Loading oddjob.");
404
405             ArooaHandler componentHandler = new DefaultComponentHandler();
406             if (persister != null) {
407                 componentHandler = new SerializedComponentHandler(
408                                 componentHandler, persister);
409             }
410             
411             ArooaFactory af = new ArooaFactory();
412             componentRegistry = af.getComponentRegistry();
413             // link this registry with parent if this is nested.
414
if (parentRegistry != null) {
415                 parentRegistry.addChild(componentRegistry, this);
416             }
417             if (persister != null) {
418                 if (persister.getRoot() == null) {
419                     persister.setRoot(this);
420                 }
421                 persister.initialise(componentRegistry);
422             }
423             
424             
425             af.setComponentFactory(getComponentFactory());
426             af.setValueFactory(getValueFactory());
427             af.setPropertProxyResolver(getPropertyProxyResolver());
428             af.setComponentProxyResolver(new OddjobComponentResolver());
429             af.setComponentHandler(componentHandler);
430             af.setBeanUtilsBean(BeanUtilsProvider.beanFor(CONVERTER_PROPERTIES));
431             af.setSubstitutionPolicy(this.getSubstitution());
432             af.setDocumentStartHandler(new OddjobHandler(new Root()));
433             af.setDocumentTag("oddjob");
434             
435             if (file != null) {
436                 af.build(file);
437             }
438             else if (input != null) {
439                 af.build(input);
440             } else {
441                 throw new OddjobException("No input specified.");
442             }
443         }
444         finally {
445             lock.release();
446         }
447     }
448     
449     /*
450      * (non-Javadoc)
451      * @see org.oddjob.jobs.AbstractJob#execute()
452      */

453     protected void execute() {
454         if (childHelper.getChild() != null) {
455             throw new OddjobException("Oddjob already loaded - reset first!");
456         }
457         ClassLoader JavaDoc existing = Thread.currentThread().getContextClassLoader();
458         if (classpath != null) {
459             classLoader = new ArooaClassLoader(existing, classpath, false);
460         }
461         if (classLoader != null) {
462             Thread.currentThread().setContextClassLoader(classLoader);
463         } else {
464             classLoader = existing;
465         }
466         try {
467             // load the xml.
468
load();
469             
470             Object JavaDoc child = childHelper.getChild();
471             
472             // if there is something to execute, execute it.
473
if (!loadOnly && child != null && child instanceof Runnable JavaDoc) {
474                 ((Runnable JavaDoc) child).run();
475             }
476             // initialise our child helper so this Oddjob job can start
477
// being influenced by the state of the child.
478
childHelper.initialise();
479             
480         }
481         finally {
482             Thread.currentThread().setContextClassLoader(existing);
483         }
484     }
485     
486     /**
487      * Actions to be performed when resetting Oddjob.
488      *
489      */

490     private void reset() {
491         // close the persister before children are removed otherwise the
492
// persisted components would be removed in the persister
493
if (persister != null) {
494             try {
495                 persister.close();
496             } catch (Exception JavaDoc e) {
497                 logger().warn("Failed closeing persister.", e);
498             }
499         }
500         // remove link to parent registry.
501
// this has to be done before destroy so that
502
// the JMSServer can update it's own registry from
503
// the childRemoved method.
504
if (parentRegistry != null) {
505             parentRegistry.removeChild(this);
506         }
507         // destroy all children.
508
childHelper.destroyAll();
509         
510     }
511     
512     public void onDestroy() {
513         reset();
514     }
515
516     /**
517      * Perform a soft reset. The super method is overridden
518      * so as not to reset the child.
519      */

520     public void softReset() {
521         lock.accquire("Soft reset in progress.");
522         try {
523             logger().debug("Thread [" + Thread.currentThread().getName()
524                     + "] soft reset for [" + getName()
525                     + "] current state " + stateHandler.getJobState());
526             if (canSoftReset()) {
527                 reset();
528             }
529         }
530         finally {
531             lock.release();
532         }
533     }
534     
535     /**
536      * Perform a hard reset. The super method is overridden
537      * so as not to reset the child but destroy them.
538      */

539     public void hardReset() {
540         lock.accquire("Hard reset in progress.");
541         try {
542             logger().debug("Thread [" + Thread.currentThread().getName()
543                     + "] hard reset for [" + getName()
544                     + "] current state " + stateHandler.getJobState());
545             if (canHardReset()) {
546                 reset();
547             }
548         }
549         finally {
550             lock.release();
551         }
552     }
553
554     /**
555      * @return Returns the persistin.
556      */

557     public ComponentPersister getPersister() {
558         return persister;
559     }
560     
561     /**
562      * @param persistin The persistin to set.
563      */

564     public void setPersister(ComponentPersister persister) {
565         this.persister = persister;
566     }
567
568     /**
569      * Lookup a component in the registry.
570      *
571      * @param path The path to the component
572      * @return The component or null if it doesn't exist.
573      */

574     public Object JavaDoc lookup(String JavaDoc path) {
575         if (componentRegistry == null) {
576             return null;
577         }
578         return componentRegistry.objectForPath(
579                 new Path(path));
580     }
581     
582     /*
583      * Custom serialization.
584      */

585     private void writeObject(ObjectOutputStream JavaDoc s)
586     throws IOException JavaDoc {
587         s.defaultWriteObject();
588     }
589     
590     /*
591      * Custom serialization.
592      */

593     private void readObject(ObjectInputStream JavaDoc s)
594     throws IOException JavaDoc, ClassNotFoundException JavaDoc {
595         s.defaultReadObject();
596     }
597
598     /**
599      * @return Returns the loadOnly.
600      */

601     public boolean isLoadOnly() {
602         return loadOnly;
603     }
604     
605     /**
606      * Set load only property.
607      *
608      * @param loadOnly true to load only, false to load and run.
609      */

610     public void setLoadOnly(boolean loadOnly) {
611         this.loadOnly = loadOnly;
612     }
613     
614     /**
615      * @return Returns the args.
616      */

617     public Object JavaDoc[] getArgs() {
618         return args;
619     }
620     
621     /**
622      * Get the args by index.
623      *
624      * @param index The index.
625      * @return The arg.
626      */

627     public Object JavaDoc getArgs(int index) {
628         if (args == null) {
629             return null;
630         }
631         if (index < args.length) {
632             return args[index];
633         }
634         return null;
635     }
636     
637     /**
638      * @param args The args to set.
639      */

640     public void setArgs(Object JavaDoc[] args) {
641         this.args = args;
642     }
643     
644     /**
645      * Set an arg by index.
646      *
647      * @param index The index
648      * @param arg The arg.
649      */

650     public void setArgs(int index, Object JavaDoc arg) {
651         if (this.args == null) {
652             this.args = new Object JavaDoc[index + 1];
653         }
654         else if (index >= args.length) {
655             Object JavaDoc[] next = new Object JavaDoc[index + 1];
656             System.arraycopy(this.args, 0, next, 0, args.length);
657             this.args = next;
658         }
659         this.args[index] = arg;
660     }
661     
662     /**
663      * A getter for Load context without a getter as this isn't
664      * a published property.
665      *
666      * @return Get the context used when Oddjob loaded the configuration
667      * file.
668      */

669     public ArooaContext loadContext() {
670         return loadContext;
671     }
672     
673     /**
674      * Get this Oddjob version.
675      *
676      * @return The version.
677      */

678     public String JavaDoc getVersion() {
679         return "@version@";
680     }
681     
682     /**
683      * The object which is the Oddjob root.
684      *
685      */

686     public class Root {
687         public void addComponent(Object JavaDoc child) {
688             logger().debug("Adding child [" + child + "]");
689             if (Oddjob.this.childHelper.getChild() != null) {
690                 throw new OddjobConfigException(
691                         "Oddjob can't have more than one child component.");
692             }
693             childHelper.addChild(child);
694         }
695     }
696     
697     /**
698      * Need our own handler to get the id element.
699      *
700      */

701     public class OddjobHandler extends ArooaHandler {
702
703         /** The root object */
704         private final Root rootComponent;
705         
706         /**
707          * Constructor.
708          */

709         public OddjobHandler(Root rootObject) {
710             this.rootComponent = rootObject;
711         }
712         
713         /**
714          * Handle the top level element.
715          */

716         public void onStartElement(String JavaDoc uri, String JavaDoc name, String JavaDoc qname,
717                                        Attributes JavaDoc attrs,
718                                        ArooaContext context)
719                 throws SAXParseException JavaDoc {
720             loadContext = context.getParent();
721             AttributeHelper ah = new AttributeHelper(uri, attrs);
722             final ComponentRegistry cr = (ComponentRegistry)
723                     context.get(ArooaConstants.COMPONENT_REGISTRY);
724             ah.process(new AttributeHelper.Processor() {
725                 /* (non-Javadoc)
726                  * @see org.oddjob.arooa.handlers.AttributeHelper.Processor#process(java.lang.String, java.lang.String)
727                  */

728                 public void process(String JavaDoc name, String JavaDoc value) {
729                     if (name.equals("id") ) {
730                         cr.register(value, Oddjob.this);
731                     }
732                 }
733             });
734
735             ArooaRuntime wrapper = new ArooaRuntime(rootComponent, name, context);
736             context.set(ArooaConstants.CURRENTLY_CONFIGURING, wrapper);
737             // so the Arooa factory will call configure for us.
738
context.getParent().set(ArooaConstants.CURRENTLY_CONFIGURING, wrapper);
739             
740             Location location = getLocation();
741             if (location == null) {
742                 // not a nested oddjob
743
Lifecycle.setContext(Oddjob.this, context);
744             }
745         }
746
747         /**
748          * Start child.
749          */

750         public ArooaHandler onStartChild(String JavaDoc uri, String JavaDoc name, String JavaDoc qname,
751                 Attributes JavaDoc attrs,
752                 ArooaContext context)
753         throws SAXParseException JavaDoc {
754             IntrospectionHelper ih = IntrospectionHelper.getHelper(rootComponent.getClass());
755             return ih.provideHandler(rootComponent, name, context);
756         }
757         
758     }
759 }
760
761
Popular Tags