KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > configuration > ConfigurationFactory


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License")
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.commons.configuration;
18
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.net.URL JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.LinkedList JavaDoc;
26 import java.util.Stack JavaDoc;
27
28 import org.apache.commons.digester.AbstractObjectCreationFactory;
29 import org.apache.commons.digester.Digester;
30 import org.apache.commons.digester.ObjectCreationFactory;
31 import org.apache.commons.digester.xmlrules.DigesterLoader;
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.xml.sax.Attributes JavaDoc;
36 import org.xml.sax.SAXException JavaDoc;
37
38 /**
39  * Factory class to create a CompositeConfiguration from a .xml file using
40  * Digester. By default it can handle the Configurations from commons-
41  * configuration. If you need to add your own, then you can pass in your own
42  * digester rules to use. It is also namespace aware, by providing a
43  * digesterRuleNamespaceURI.
44  *
45  * @author <a HREF="mailto:epugh@upstate.com">Eric Pugh</a>
46  * @author <a HREF="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
47  * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
48  * @version $Id: ConfigurationFactory.java 155408 2005-02-26 12:56:39Z dirkv $
49  */

50 public class ConfigurationFactory
51 {
52     /** Constant for the root element in the info file.*/
53     private static final String JavaDoc SEC_ROOT = "configuration/";
54
55     /** Constant for the override section.*/
56     private static final String JavaDoc SEC_OVERRIDE = SEC_ROOT + "override/";
57
58     /** Constant for the additional section.*/
59     private static final String JavaDoc SEC_ADDITIONAL = SEC_ROOT + "additional/";
60     
61     /** Constant for the optional attribute.*/
62     private static final String JavaDoc ATTR_OPTIONAL = "optional";
63     
64     /** Constant for the fileName attribute.*/
65     private static final String JavaDoc ATTR_FILENAME = "fileName";
66
67     /** Constant for the default base path (points to actual directory).*/
68     private static final String JavaDoc DEF_BASE_PATH = ".";
69
70     /** static logger */
71     private static Log log = LogFactory.getLog(ConfigurationFactory.class);
72
73     /** The XML file with the details about the configuration to load */
74     private String JavaDoc configurationFileName;
75
76     /** The URL to the XML file with the details about the configuration to load. */
77     private URL JavaDoc configurationURL;
78
79     /**
80      * The implicit base path for included files. This path is determined by
81      * the configuration to load and used unless no other base path was
82      * explicitely specified.
83      */

84     private String JavaDoc implicitBasePath;
85
86     /** The basePath to prefix file paths for file based property files. */
87     private String JavaDoc basePath;
88
89     /** URL for xml digester rules file */
90     private URL JavaDoc digesterRules;
91
92     /** The digester namespace to parse */
93     private String JavaDoc digesterRuleNamespaceURI;
94
95     /**
96      * Constructor
97      */

98     public ConfigurationFactory()
99     {
100         setBasePath(DEF_BASE_PATH);
101     }
102     /**
103      * Constructor with ConfigurationFile Name passed
104      *
105      * @param configurationFileName The path to the configuration file
106      */

107     public ConfigurationFactory(String JavaDoc configurationFileName)
108     {
109         this.configurationFileName = configurationFileName;
110     }
111
112     /**
113      * Return the configuration provided by this factory. It loads the
114      * configuration file which is a XML description of the actual
115      * configurations to load. It can contain various different types of
116      * configuration, currently Properties, XML and JNDI.
117      *
118      * @return A Configuration object
119      * @throws ConfigurationException A generic exception that we had trouble during the
120      * loading of the configuration data.
121      */

122     public Configuration getConfiguration() throws ConfigurationException
123     {
124         Digester digester;
125         InputStream JavaDoc input = null;
126         ConfigurationBuilder builder = new ConfigurationBuilder();
127         URL JavaDoc url = getConfigurationURL();
128         try
129         {
130             if (url == null)
131             {
132                 url = ConfigurationUtils.locate(implicitBasePath, getConfigurationFileName());
133             }
134             input = url.openStream();
135         }
136         catch (Exception JavaDoc e)
137         {
138             log.error("Exception caught opening stream to URL", e);
139             throw new ConfigurationException("Exception caught opening stream to URL", e);
140         }
141
142         if (getDigesterRules() == null)
143         {
144             digester = new Digester();
145             configureNamespace(digester);
146             initDefaultDigesterRules(digester);
147         }
148         else
149         {
150             digester = DigesterLoader.createDigester(getDigesterRules());
151             // This might already be too late. As far as I can see, the namespace
152
// awareness must be configured before the digester rules are loaded.
153
configureNamespace(digester);
154         }
155         
156         // Configure digester to always enable the context class loader
157
digester.setUseContextClassLoader(true);
158         // Put the composite builder object below all of the other objects.
159
digester.push(builder);
160         // Parse the input stream to configure our mappings
161
try
162         {
163             digester.parse(input);
164             input.close();
165         }
166         catch (SAXException JavaDoc saxe)
167         {
168             log.error("SAX Exception caught", saxe);
169             throw new ConfigurationException("SAX Exception caught", saxe);
170         }
171         catch (IOException JavaDoc ioe)
172         {
173             log.error("IO Exception caught", ioe);
174             throw new ConfigurationException("IO Exception caught", ioe);
175         }
176         return builder.getConfiguration();
177     }
178
179     /**
180      * Returns the configurationFile.
181      *
182      * @return The name of the configuration file. Can be null.
183      */

184     public String JavaDoc getConfigurationFileName()
185     {
186         return configurationFileName;
187     }
188
189     /**
190      * Sets the configurationFile.
191      *
192      * @param configurationFileName The name of the configurationFile to use.
193      */

194     public void setConfigurationFileName(String JavaDoc configurationFileName)
195     {
196         File JavaDoc file = new File JavaDoc(configurationFileName).getAbsoluteFile();
197         this.configurationFileName = file.getName();
198         implicitBasePath = file.getParent();
199     }
200
201     /**
202      * Returns the URL of the configuration file to be loaded.
203      *
204      * @return the URL of the configuration to load
205      */

206     public URL JavaDoc getConfigurationURL()
207     {
208         return configurationURL;
209     }
210
211     /**
212      * Sets the URL of the configuration to load. This configuration can be
213      * either specified by a file name or by a URL.
214      *
215      * @param url the URL of the configuration to load
216      */

217     public void setConfigurationURL(URL JavaDoc url)
218     {
219         configurationURL = url;
220         implicitBasePath = url.toString();
221
222         // The following is a hack caused by the need to keep backwards
223
// compatibility: Per default the base path is set to the current
224
// directory. For loading from a URL this makes no sense. So
225
// unless no specific base path was set we clear it.
226
if (DEF_BASE_PATH.equals(getBasePath()))
227         {
228             setBasePath(null);
229         }
230     }
231
232     /**
233      * Returns the digesterRules.
234      *
235      * @return URL
236      */

237     public URL JavaDoc getDigesterRules()
238     {
239         return digesterRules;
240     }
241
242     /**
243      * Sets the digesterRules.
244      *
245      * @param digesterRules The digesterRules to set
246      */

247     public void setDigesterRules(URL JavaDoc digesterRules)
248     {
249         this.digesterRules = digesterRules;
250     }
251
252     /**
253      * Initializes the parsing rules for the default digester
254      *
255      * This allows the Configuration Factory to understand the
256      * default types: Properties, XML and JNDI. Two special sections are
257      * introduced: <code>&lt;override&gt;</code> and
258      * <code>&lt;additional&gt;</code>.
259      *
260      * @param digester The digester to configure
261      */

262     protected void initDefaultDigesterRules(Digester digester)
263     {
264         initDigesterSectionRules(digester, SEC_ROOT, false);
265         initDigesterSectionRules(digester, SEC_OVERRIDE, false);
266         initDigesterSectionRules(digester, SEC_ADDITIONAL, true);
267     }
268
269     /**
270      * Sets up digester rules for a specified section of the configuration
271      * info file.
272      *
273      * @param digester the current digester instance
274      * @param matchString specifies the section
275      * @param additional a flag if rules for the additional section are to be
276      * added
277      */

278     protected void initDigesterSectionRules(Digester digester, String JavaDoc matchString, boolean additional)
279     {
280         setupDigesterInstance(
281             digester,
282             matchString + "properties",
283             new FileConfigurationFactory(PropertiesConfiguration.class),
284             null,
285             additional);
286
287         setupDigesterInstance(
288             digester,
289             matchString + "xml",
290             new FileConfigurationFactory(XMLConfiguration.class),
291             null,
292             additional);
293
294         setupDigesterInstance(
295             digester,
296             matchString + "hierarchicalXml",
297             new FileConfigurationFactory(XMLConfiguration.class),
298             null,
299             additional);
300
301         setupDigesterInstance(
302             digester,
303             matchString + "jndi",
304             new JNDIConfigurationFactory(),
305             null,
306             additional);
307
308         setupDigesterInstance(
309             digester,
310             matchString + "system",
311             new SystemConfigurationFactory(),
312             null,
313             additional);
314     }
315
316     /**
317      * Sets up digester rules for a configuration to be loaded.
318      *
319      * @param digester the current digester
320      * @param matchString the pattern to match with this rule
321      * @param factory an ObjectCreationFactory instance to use for creating new
322      * objects
323      * @param method the name of a method to be called or <b>null</b> for none
324      * @param additional a flag if rules for the additional section are to be
325      * added
326      */

327     protected void setupDigesterInstance(
328             Digester digester,
329             String JavaDoc matchString,
330             ObjectCreationFactory factory,
331             String JavaDoc method,
332             boolean additional)
333     {
334         if (additional)
335         {
336             setupUnionRules(digester, matchString);
337         }
338         digester.addFactoryCreate(matchString, factory);
339         digester.addSetProperties(matchString);
340         if (method != null)
341         {
342             digester.addCallMethod(matchString, method);
343         }
344         digester.addSetNext(
345             matchString,
346             "addConfiguration",
347             Configuration.class.getName());
348     }
349
350     /**
351      * Sets up rules for configurations in the additional section.
352      *
353      * @param digester the current digester
354      * @param matchString the pattern to match with this rule
355      */

356     protected void setupUnionRules(Digester digester, String JavaDoc matchString)
357     {
358         digester.addObjectCreate(matchString,
359         AdditionalConfigurationData.class);
360         digester.addSetProperties(matchString);
361         digester.addSetNext(matchString, "addAdditionalConfig",
362         AdditionalConfigurationData.class.getName());
363     }
364
365     /**
366      * Returns the digesterRuleNamespaceURI.
367      *
368      * @return A String with the digesterRuleNamespaceURI.
369      */

370     public String JavaDoc getDigesterRuleNamespaceURI()
371     {
372         return digesterRuleNamespaceURI;
373     }
374
375     /**
376      * Sets the digesterRuleNamespaceURI.
377      *
378      * @param digesterRuleNamespaceURI The new digesterRuleNamespaceURI to use
379      */

380     public void setDigesterRuleNamespaceURI(String JavaDoc digesterRuleNamespaceURI)
381     {
382         this.digesterRuleNamespaceURI = digesterRuleNamespaceURI;
383     }
384
385     /**
386      * Configure the current digester to be namespace aware and to have
387      * a Configuration object to which all of the other configurations
388      * should be added
389      *
390      * @param digester The Digester to configure
391      */

392     private void configureNamespace(Digester digester)
393     {
394         if (getDigesterRuleNamespaceURI() != null)
395         {
396             digester.setNamespaceAware(true);
397             digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI());
398         }
399         else
400         {
401             digester.setNamespaceAware(false);
402         }
403         digester.setValidating(false);
404     }
405
406     /**
407      * Returns the Base path from which this Configuration Factory operates.
408      * This is never null. If you set the BasePath to null, then a base path
409      * according to the configuration to load is returned.
410      *
411      * @return The base Path of this configuration factory.
412      */

413     public String JavaDoc getBasePath()
414     {
415         String JavaDoc path = StringUtils.isEmpty(basePath) ? implicitBasePath : basePath;
416         return StringUtils.isEmpty(path) ? "." : path;
417     }
418
419     /**
420      * Sets the basePath for all file references from this Configuration Factory.
421      * Normally a base path need not to be set because it is determined by
422      * the location of the configuration file to load. All relative pathes in
423      * this file are resolved relative to this file. Setting a base path makes
424      * sense if such relative pathes should be otherwise resolved, e.g. if
425      * the configuration file is loaded from the class path and all sub
426      * configurations it refers to are stored in a special config directory.
427      *
428      * @param basePath The new basePath to set.
429      */

430     public void setBasePath(String JavaDoc basePath)
431     {
432         this.basePath = basePath;
433     }
434
435     /**
436      * A base class for digester factory classes. This base class maintains
437      * a default class for the objects to be created.
438      * There will be sub classes for specific configuration implementations.
439      */

440     public class DigesterConfigurationFactory extends AbstractObjectCreationFactory
441     {
442         /** Actual class to use. */
443         private Class JavaDoc clazz;
444
445         /**
446          * Creates a new instance of <code>DigesterConfigurationFactory</code>.
447          *
448          * @param clazz the class which we should instantiate
449          */

450         public DigesterConfigurationFactory(Class JavaDoc clazz)
451         {
452             this.clazz = clazz;
453         }
454
455         /**
456          * Creates an instance of the specified class.
457          *
458          * @param attribs the attributes (ignored)
459          * @return the new object
460          * @throws Exception if object creation fails
461          */

462         public Object JavaDoc createObject(Attributes JavaDoc attribs) throws Exception JavaDoc
463         {
464             return clazz.newInstance();
465         }
466     }
467
468     /**
469      * A tiny inner class that allows the Configuration Factory to
470      * let the digester construct FileConfiguration objects
471      * that already have the correct base Path set.
472      *
473      */

474     public class FileConfigurationFactory extends DigesterConfigurationFactory
475     {
476         /**
477          * C'tor
478          *
479          * @param clazz The class which we should instantiate.
480          */

481         public FileConfigurationFactory(Class JavaDoc clazz)
482         {
483             super(clazz);
484         }
485
486         /**
487          * Gets called by the digester.
488          *
489          * @param attributes the actual attributes
490          * @return the new object
491          * @throws Exception Couldn't instantiate the requested object.
492          */

493         public Object JavaDoc createObject(Attributes JavaDoc attributes) throws Exception JavaDoc
494         {
495             FileConfiguration conf = (FileConfiguration) super.createObject(attributes);
496             conf.setBasePath(getBasePath());
497             conf.setFileName(attributes.getValue(ATTR_FILENAME));
498             try
499             {
500                 log.info("Trying to load configuration " + conf.getFileName());
501                 conf.load();
502             }
503             catch(ConfigurationException cex)
504             {
505                 if(attributes.getValue(ATTR_OPTIONAL) != null
506                         && PropertyConverter.toBoolean(attributes.getValue(ATTR_OPTIONAL)).booleanValue())
507                 {
508                     log.warn("Could not load optional configuration " + conf.getFileName());
509                 }
510                 else
511                 {
512                     throw cex;
513                 }
514             }
515             return conf;
516         }
517     }
518
519     /**
520      * A tiny inner class that allows the Configuration Factory to
521      * let the digester construct JNDIConfiguration objects.
522      */

523     private class JNDIConfigurationFactory extends DigesterConfigurationFactory
524     {
525         public JNDIConfigurationFactory()
526         {
527             super(JNDIConfiguration.class);
528         }
529     }
530
531     /**
532      * A tiny inner class that allows the Configuration Factory to
533      * let the digester construct SystemConfiguration objects.
534      */

535     private class SystemConfigurationFactory extends DigesterConfigurationFactory
536     {
537         public SystemConfigurationFactory()
538         {
539             super(SystemConfiguration.class);
540         }
541     }
542
543     /**
544      * A simple data class that holds all information about a configuration
545      * from the <code>&lt;additional&gt;</code> section.
546      */

547     public static class AdditionalConfigurationData
548     {
549         /** Stores the configuration object.*/
550         private Configuration configuration;
551
552         /** Stores the location of this configuration in the global tree.*/
553         private String JavaDoc at;
554
555         /**
556          * Returns the value of the <code>at</code> attribute.
557          *
558          * @return the at attribute
559          */

560         public String JavaDoc getAt()
561         {
562             return at;
563         }
564
565         /**
566          * Sets the value of the <code>at</code> attribute.
567          *
568          * @param string the attribute value
569          */

570         public void setAt(String JavaDoc string)
571         {
572             at = string;
573         }
574
575         /**
576          * Returns the configuration object.
577          *
578          * @return the configuration
579          */

580         public Configuration getConfiguration()
581         {
582             return configuration;
583         }
584
585         /**
586          * Sets the configuration object. Note: Normally this method should be
587          * named <code>setConfiguration()</code>, but the name
588          * <code>addConfiguration()</code> is required by some of the digester
589          * rules.
590          *
591          * @param config the configuration to set
592          */

593         public void addConfiguration(Configuration config)
594         {
595             configuration = config;
596         }
597     }
598
599     /**
600      * An internally used helper class for constructing the composite
601      * configuration object.
602      */

603     public static class ConfigurationBuilder
604     {
605         /** Stores the composite configuration.*/
606         private CompositeConfiguration config;
607
608         /** Stores a collection with the configs from the additional section.*/
609         private Collection JavaDoc additionalConfigs;
610
611         /**
612          * Creates a new instance of <code>ConfigurationBuilder</code>.
613          */

614         public ConfigurationBuilder()
615         {
616             config = new CompositeConfiguration();
617             additionalConfigs = new LinkedList JavaDoc();
618         }
619
620         /**
621          * Adds a new configuration to this object. This method is called by
622          * Digester.
623          *
624          * @param conf the configuration to be added
625          */

626         public void addConfiguration(Configuration conf)
627         {
628             config.addConfiguration(conf);
629         }
630
631         /**
632          * Adds information about an additional configuration. This method is
633          * called by Digester.
634          *
635          * @param data the data about the additional configuration
636          */

637         public void addAdditionalConfig(AdditionalConfigurationData data)
638         {
639             additionalConfigs.add(data);
640         }
641
642         /**
643          * Returns the final composite configuration.
644          *
645          * @return the final configuration object
646          */

647         public CompositeConfiguration getConfiguration()
648         {
649             if (!additionalConfigs.isEmpty())
650             {
651                 Configuration unionConfig = createAdditionalConfiguration(additionalConfigs);
652                 if (unionConfig != null)
653                 {
654                     addConfiguration(unionConfig);
655                 }
656                 additionalConfigs.clear();
657             }
658
659             return config;
660         }
661
662         /**
663          * Creates a configuration object with the union of all properties
664          * defined in the <code>&lt;additional&gt;</code> section. This
665          * implementation returns a <code>HierarchicalConfiguration</code>
666          * object.
667          *
668          * @param configs a collection with
669          * <code>AdditionalConfigurationData</code> objects
670          * @return the union configuration (can be <b>null</b>)
671          */

672         protected Configuration createAdditionalConfiguration(Collection JavaDoc configs)
673         {
674             HierarchicalConfiguration result = new HierarchicalConfiguration();
675
676             for (Iterator JavaDoc it = configs.iterator(); it.hasNext();)
677             {
678                 AdditionalConfigurationData cdata =
679                 (AdditionalConfigurationData) it.next();
680                 result.addNodes(cdata.getAt(),
681                 createRootNode(cdata).getChildren());
682             }
683
684             return result.isEmpty() ? null : result;
685         }
686
687         /**
688          * Creates a configuration root node for the specified configuration.
689          *
690          * @param cdata the configuration data object
691          * @return a root node for this configuration
692          */

693         private HierarchicalConfiguration.Node createRootNode(AdditionalConfigurationData cdata)
694         {
695             if (cdata.getConfiguration() instanceof HierarchicalConfiguration)
696             {
697                 // we can directly use this configuration's root node
698
return ((HierarchicalConfiguration) cdata.getConfiguration()).getRoot();
699             }
700             else
701             {
702                 // transform configuration to a hierarchical root node
703
HierarchicalConfigurationNodeConverter conv =
704                 new HierarchicalConfigurationNodeConverter();
705                 conv.process(cdata.getConfiguration());
706                 return conv.getRootNode();
707             }
708         }
709     }
710
711     /**
712      * A specialized <code>HierarchicalConfigurationConverter</code> class
713      * that creates a <code>HierarchicalConfiguration</code> root node from
714      * an arbitrary <code>Configuration</code> object. This class is used to
715      * add additional configuration objects to the hierarchical configuration
716      * managed by the <code>ConfigurationBuilder</code>.
717      */

718     static class HierarchicalConfigurationNodeConverter extends HierarchicalConfigurationConverter
719     {
720         /** A stack for constructing the hierarchy.*/
721         private Stack JavaDoc nodes;
722
723         /** Stores the root node.*/
724         private HierarchicalConfiguration.Node root;
725
726         /**
727          * Default constructor.
728          */

729         public HierarchicalConfigurationNodeConverter()
730         {
731             nodes = new Stack JavaDoc();
732             root = new HierarchicalConfiguration.Node();
733             nodes.push(root);
734         }
735
736         /**
737          * Callback for an element start event. Creates a new node and adds
738          * it to the actual parent.
739          *
740          * @param name the name of the new node
741          * @param value the node's value
742          */

743         protected void elementStart(String JavaDoc name, Object JavaDoc value)
744         {
745             HierarchicalConfiguration.Node parent = (HierarchicalConfiguration.Node) nodes.peek();
746             HierarchicalConfiguration.Node child = new HierarchicalConfiguration.Node(name);
747             if (value != null)
748             {
749                 child.setValue(value);
750             }
751             parent.addChild(child);
752             nodes.push(child);
753         }
754
755         /**
756          * Callback for an element end event. Clears the stack.
757          *
758          * @param name the name of the element
759          */

760         protected void elementEnd(String JavaDoc name)
761         {
762             nodes.pop();
763         }
764
765         /**
766          * Returns the constructed root node.
767          *
768          * @return the root node
769          */

770         public HierarchicalConfiguration.Node getRootNode()
771         {
772             return root;
773         }
774     }
775 }
776
Popular Tags