KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > conf > Configuration


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.conf;
57
58 import java.io.InputStream JavaDoc;
59 import java.io.IOException JavaDoc;
60 import java.net.URL JavaDoc;
61 import java.util.Collection JavaDoc;
62 import java.util.Collections JavaDoc;
63 import java.util.Iterator JavaDoc;
64 import java.util.Map JavaDoc;
65 import java.util.HashMap JavaDoc;
66 import java.util.List JavaDoc;
67 import java.util.ArrayList JavaDoc;
68
69 import org.apache.commons.lang.Validate;
70 import org.apache.commons.collections.Predicate;
71 import org.apache.log4j.BasicConfigurator;
72 import org.apache.log4j.Level;
73 import org.apache.log4j.Logger;
74 import org.apache.log4j.PropertyConfigurator;
75 import org.objectstyle.cayenne.CayenneRuntimeException;
76 import org.objectstyle.cayenne.ConfigurationException;
77 import org.objectstyle.cayenne.access.DataDomain;
78 import org.objectstyle.cayenne.util.CayenneMap;
79 import org.objectstyle.cayenne.util.ResourceLocator;
80 import org.objectstyle.cayenne.dataview.DataView;
81
82 /**
83  * This class is an entry point to Cayenne. It loads all configuration files and
84  * instantiates main Cayenne objects. Used as a singleton via the
85  * {@link #getSharedConfiguration}method.
86  * <p>
87  * To use a custom subclass of Configuration, Java applications must call
88  * {@link #initializeSharedConfiguration}with the subclass as argument. This will create
89  * and initialize a Configuration singleton instance of the specified class. By default
90  * {@link DefaultConfiguration}is instantiated.
91  * </p>
92  *
93  * @author Andrei Adamchik
94  * @author Holger Hoffstaette
95  */

96 public abstract class Configuration {
97
98     private static Logger logObj = Logger.getLogger(Configuration.class);
99
100     public static final String JavaDoc DEFAULT_LOGGING_PROPS_FILE = ".cayenne/cayenne-log.properties";
101     public static final String JavaDoc DEFAULT_DOMAIN_FILE = "cayenne.xml";
102     public static final Class JavaDoc DEFAULT_CONFIGURATION_CLASS = DefaultConfiguration.class;
103
104     protected static Configuration sharedConfiguration = null;
105     private static boolean loggingConfigured = false;
106
107     public static final Predicate ACCEPT_ALL_DATAVIEWS = new Predicate() {
108
109         public boolean evaluate(Object JavaDoc dataViewName) {
110             return true;
111         }
112     };
113
114     /**
115      * Defines a ClassLoader to use for resource lookup. Configuration objects that are
116      * using ClassLoaders to locate resources may need to be bootstrapped explicitly.
117      */

118     protected static ClassLoader JavaDoc resourceLoader;
119
120     /**
121      * Stores ClassLoader set up for the current thread.
122      *
123      * @since 1.2
124      */

125     protected static ThreadLocal JavaDoc threadClassLoader = new ThreadLocal JavaDoc();
126
127     /** Lookup map that stores DataDomains with names as keys. */
128     protected CayenneMap dataDomains = new CayenneMap(this);
129     protected Collection JavaDoc dataDomainsRef = Collections.unmodifiableCollection(dataDomains
130             .values());
131     protected DataSourceFactory overrideFactory;
132     protected ConfigStatus loadStatus = new ConfigStatus();
133     protected String JavaDoc domainConfigurationName = DEFAULT_DOMAIN_FILE;
134     protected boolean ignoringLoadFailures;
135     protected ConfigLoaderDelegate loaderDelegate;
136     protected ConfigurationShutdownHook configurationShutdownHook = new ConfigurationShutdownHook();
137     protected Map JavaDoc dataViewLocations = new HashMap JavaDoc();
138     protected String JavaDoc projectVersion;
139     
140     /**
141      * Stores instance ClassLoader.
142      *
143      * @since 1.2
144      */

145     protected ClassLoader JavaDoc classLoader;
146
147     /**
148      * Sets <code>cl</code> class's ClassLoader to serve as shared configuration
149      * resource ClassLoader. If shared Configuration object does not use ClassLoader, this
150      * method call will have no effect on how resources are loaded.
151      */

152     public static void bootstrapSharedConfiguration(Class JavaDoc cl) {
153         if (cl.getClassLoader() != null) {
154             resourceLoader = cl.getClassLoader();
155         }
156         else {
157             logObj
158                     .debug("An attempt to bootstrap configuration with null class loader for class "
159                             + cl.getName());
160         }
161     }
162
163     /**
164      * Configures Cayenne logging properties. Search for the properties file called
165      * <code>cayenne-log.properties</code> is first done in $HOME/.cayenne, then in
166      * CLASSPATH.
167      */

168     public synchronized static void configureCommonLogging() {
169         if (!Configuration.isLoggingConfigured()) {
170             // create a simple CLASSPATH/$HOME locator
171
ResourceLocator locator = new ResourceLocator();
172             locator.setSkipAbsolutePath(true);
173             locator.setSkipClasspath(false);
174             locator.setSkipCurrentDirectory(true);
175             locator.setSkipHomeDirectory(false);
176
177             // and load the default logging config file
178
URL JavaDoc configURL = locator.findResource(DEFAULT_LOGGING_PROPS_FILE);
179             Configuration.configureCommonLogging(configURL);
180         }
181     }
182
183     /**
184      * Configures Cayenne logging properties using properties found at the specified URL.
185      */

186     public synchronized static void configureCommonLogging(URL JavaDoc propsFile) {
187         if (!Configuration.isLoggingConfigured()) {
188             if (propsFile != null) {
189                 PropertyConfigurator.configure(propsFile);
190                 logObj.debug("configured log4j from: " + propsFile);
191             }
192             else {
193                 BasicConfigurator.configure();
194                 logObj.debug("configured log4j with BasicConfigurator.");
195             }
196
197             // remember configuration success
198
Configuration.setLoggingConfigured(true);
199         }
200     }
201
202     /**
203      * Indicates whether Log4j has been initialized, either by cayenne or otherwise. If an
204      * external setup has been detected, {@link #setLoggingConfigured}will be called to
205      * remember this.
206      */

207     public static boolean isLoggingConfigured() {
208         if (!loggingConfigured) {
209             // check for existing log4j setup
210
if (Logger.getRootLogger().getAllAppenders().hasMoreElements()) {
211                 Configuration.setLoggingConfigured(true);
212             }
213         }
214
215         return loggingConfigured;
216     }
217
218     /**
219      * Indicate whether Log4j has been initialized. Can be used when subclasses customize
220      * the initialization process, or to configure Log4J outside of Cayenne.
221      */

222     public synchronized static void setLoggingConfigured(boolean state) {
223         loggingConfigured = state;
224     }
225
226     /**
227      * Use this method as an entry point to all Cayenne access objects.
228      * <p>
229      * Note that if you want to provide a custom Configuration, make sure you call one of
230      * the {@link #initializeSharedConfiguration}methods before your application code has
231      * a chance to call this method.
232      */

233     public synchronized static Configuration getSharedConfiguration() {
234         if (Configuration.sharedConfiguration == null) {
235             Configuration.initializeSharedConfiguration();
236         }
237
238         return Configuration.sharedConfiguration;
239     }
240
241     /**
242      * Returns the ClassLoader used to load resources. Since Cayenne 1.2 this method
243      * implements different logic for providing a class loader. First it checks whether a
244      * thread-local ClassLoader was provided via "setThreadClassLoader", then it checked
245      * static resourceLoader, and finally it returns current thread context ClassLoader.
246      */

247     public static ClassLoader JavaDoc getResourceLoader() {
248         ClassLoader JavaDoc loader = (ClassLoader JavaDoc) threadClassLoader.get();
249         if (loader == null) {
250             loader = Configuration.resourceLoader;
251         }
252
253         if (loader == null) {
254             loader = Thread.currentThread().getContextClassLoader();
255         }
256
257         return loader;
258     }
259
260     public static void setThreadClassLoader(ClassLoader JavaDoc classLoader) {
261         threadClassLoader.set(classLoader);
262     }
263
264     /**
265      * Returns default log level for loading configuration. Log level is made static so
266      * that applications can set it before shared Configuration object is instantiated.
267      */

268     public static Level getLoggingLevel() {
269         Level l = logObj.getLevel();
270         return (l != null ? l : Level.DEBUG);
271     }
272
273     /**
274      * Sets the default log level for loading a configuration.
275      */

276     public static void setLoggingLevel(Level logLevel) {
277         logObj.setLevel(logLevel);
278     }
279
280     /**
281      * Creates and initializes shared Configuration object. By default
282      * {@link DefaultConfiguration}will be instantiated and assigned to a singleton
283      * instance of Configuration.
284      */

285     public static void initializeSharedConfiguration() {
286         Configuration.initializeSharedConfiguration(DEFAULT_CONFIGURATION_CLASS);
287     }
288
289     /**
290      * Creates and initializes a shared Configuration object of a custom Configuration
291      * subclass.
292      */

293     public static void initializeSharedConfiguration(Class JavaDoc configurationClass) {
294         Configuration conf = null;
295
296         try {
297             conf = (Configuration) configurationClass.newInstance();
298         }
299         catch (Exception JavaDoc ex) {
300             logObj.error("Error creating shared Configuration: ", ex);
301             throw new ConfigurationException("Error creating shared Configuration."
302                     + ex.getMessage(), ex);
303         }
304
305         Configuration.initializeSharedConfiguration(conf);
306     }
307
308     /**
309      * Sets the shared Configuration object to a new Configuration object. First calls
310      * {@link #canInitialize}and - if permitted -{@link #initialize}followed by
311      * {@link #didInitialize}.
312      */

313     public static void initializeSharedConfiguration(Configuration conf) {
314         // check to see whether we can proceed
315
if (!conf.canInitialize()) {
316             throw new ConfigurationException("Configuration of class "
317                     + conf.getClass().getName()
318                     + " refused to be initialized.");
319         }
320
321         try {
322             // initialize configuration
323
conf.initialize();
324
325             // call post-initialization hook
326
conf.didInitialize();
327
328             // set the initialized Configuration only after success
329
Configuration.sharedConfiguration = conf;
330         }
331         catch (Exception JavaDoc ex) {
332             throw new ConfigurationException(
333                     "Error during Configuration initialization. " + ex.getMessage(),
334                     ex);
335         }
336     }
337
338     /**
339      * Default constructor for new Configuration instances. Simply calls
340      * {@link Configuration#Configuration(String)}.
341      *
342      * @see Configuration#Configuration(String)
343      */

344     protected Configuration() {
345         this(DEFAULT_DOMAIN_FILE);
346     }
347
348     /**
349      * Default constructor for new Configuration instances using the given resource name
350      * as the main domain file. First calls {@link #configureLogging}, then
351      * {@link #setDomainConfigurationName}with the given domain configuration resource
352      * name.
353      */

354     protected Configuration(String JavaDoc domainConfigurationName) {
355         super();
356
357         // set up logging
358
this.configureLogging();
359
360         // set domain configuration name
361
this.setDomainConfigurationName(domainConfigurationName);
362     }
363
364     /**
365      * Indicates whether {@link #initialize}can be called. Returning <code>false</code>
366      * allows new instances to delay or refuse the initialization process.
367      */

368     public abstract boolean canInitialize();
369
370     /**
371      * Initializes the new instance.
372      *
373      * @throws Exception
374      */

375     public abstract void initialize() throws Exception JavaDoc;
376
377     /**
378      * Called after successful completion of {@link #initialize}.
379      */

380     public abstract void didInitialize();
381
382     /**
383      * Returns the resource locator used for finding and loading resources.
384      */

385     protected abstract ResourceLocator getResourceLocator();
386
387     /**
388      * Returns a DataDomain as a stream or <code>null</code> if it cannot be found.
389      */

390     // TODO: this method is only used in sublcass (DefaultConfiguration),
391
// should we remove it from here?
392
protected abstract InputStream JavaDoc getDomainConfiguration();
393
394     /**
395      * Returns a DataMap with the given name or <code>null</code> if it cannot be found.
396      */

397     protected abstract InputStream JavaDoc getMapConfiguration(String JavaDoc name);
398
399     protected abstract InputStream JavaDoc getViewConfiguration(String JavaDoc location);
400
401     /**
402      * Configures log4J. This implementation calls
403      * {@link Configuration#configureCommonLogging}.
404      */

405     protected void configureLogging() {
406         Configuration.configureCommonLogging();
407     }
408
409     /**
410      * Returns the name of the main domain configuration resource. Defaults to
411      * {@link Configuration#DEFAULT_DOMAIN_FILE}.
412      */

413     public String JavaDoc getDomainConfigurationName() {
414         return this.domainConfigurationName;
415     }
416
417     /**
418      * Sets the name of the main domain configuration resource.
419      *
420      * @param domainConfigurationName the name of the resource that contains this
421      * Configuration's domain(s).
422      */

423     protected void setDomainConfigurationName(String JavaDoc domainConfigurationName) {
424         this.domainConfigurationName = domainConfigurationName;
425     }
426
427     /**
428      * @since 1.1
429      */

430     public String JavaDoc getProjectVersion() {
431         return projectVersion;
432     }
433
434     /**
435      * @since 1.1
436      */

437     public void setProjectVersion(String JavaDoc projectVersion) {
438         this.projectVersion = projectVersion;
439     }
440
441     /**
442      * Returns an internal property for the DataSource factory that will override any
443      * settings configured in XML. Subclasses may override this method to provide a
444      * special factory for DataSource creation that will take precedence over any
445      * factories configured in a cayenne project.
446      */

447     public DataSourceFactory getDataSourceFactory() {
448         return this.overrideFactory;
449     }
450
451     public void setDataSourceFactory(DataSourceFactory overrideFactory) {
452         this.overrideFactory = overrideFactory;
453     }
454
455     /**
456      * Adds new DataDomain to the list of registered domains.
457      */

458     public void addDomain(DataDomain domain) {
459         this.dataDomains.put(domain.getName(), domain);
460         logObj.debug("added domain: " + domain.getName());
461     }
462
463     /**
464      * Returns registered domain matching <code>name</code> or <code>null</code> if no
465      * such domain is found.
466      */

467     public DataDomain getDomain(String JavaDoc name) {
468         return (DataDomain) this.dataDomains.get(name);
469     }
470
471     /**
472      * Returns default domain of this configuration. If no domains are configured,
473      * <code>null</code> is returned. If more than one domain exists in this
474      * configuration, a CayenneRuntimeException is thrown, indicating that the domain name
475      * must be explicitly specified. In such cases {@link #getDomain(String name)}must be
476      * used instead.
477      */

478     public DataDomain getDomain() {
479         int size = this.dataDomains.size();
480         if (size == 0) {
481             return null;
482         }
483         else if (size == 1) {
484             return (DataDomain) this.dataDomains.values().iterator().next();
485         }
486         else {
487             throw new CayenneRuntimeException(
488                     "More than one domain is configured; use 'getDomain(String name)' instead.");
489         }
490     }
491
492     /**
493      * Unregisters DataDomain matching <code>name<code> from
494      * this Configuration object. Note that any domain database
495      * connections remain open, and it is a responsibility of a
496      * caller to clean it up.
497      */

498     public void removeDomain(String JavaDoc name) {
499         this.dataDomains.remove(name);
500         logObj.debug("removed domain: " + name);
501     }
502
503     /**
504      * Returns an unmodifiable collection of registered {@link DataDomain}objects.
505      */

506     public Collection JavaDoc getDomains() {
507         return this.dataDomainsRef;
508     }
509
510     /**
511      * Returns whether to ignore any failures during map loading or not.
512      *
513      * @return boolean
514      */

515     public boolean isIgnoringLoadFailures() {
516         return this.ignoringLoadFailures;
517     }
518
519     /**
520      * Sets whether to ignore any failures during map loading or not.
521      *
522      * @param ignoringLoadFailures <code>true</code> or <code>false</code>
523      */

524     protected void setIgnoringLoadFailures(boolean ignoringLoadFailures) {
525         this.ignoringLoadFailures = ignoringLoadFailures;
526     }
527
528     /**
529      * Returns the load status.
530      *
531      * @return ConfigStatus
532      */

533     public ConfigStatus getLoadStatus() {
534         return this.loadStatus;
535     }
536
537     /**
538      * Sets the load status.
539      */

540     protected void setLoadStatus(ConfigStatus status) {
541         this.loadStatus = status;
542     }
543
544     /**
545      * Returns a delegate used for controlling the loading of configuration elements.
546      */

547     public ConfigLoaderDelegate getLoaderDelegate() {
548         return loaderDelegate;
549     }
550
551     /**
552      * @since 1.1
553      * @param loaderDelegate
554      */

555     public void setLoaderDelegate(ConfigLoaderDelegate loaderDelegate) {
556         this.loaderDelegate = loaderDelegate;
557     }
558
559     /**
560      * Initializes configuration with the location of data views.
561      *
562      * @since 1.1
563      * @param dataViewLocations Map of DataView locations.
564      */

565     public void setDataViewLocations(Map JavaDoc dataViewLocations) {
566         if (dataViewLocations == null)
567             this.dataViewLocations = new HashMap JavaDoc();
568         else
569             this.dataViewLocations = dataViewLocations;
570     }
571
572     /**
573      * @since 1.1
574      */

575     public Map JavaDoc getDataViewLocations() {
576         return dataViewLocations;
577     }
578
579     /**
580      * @since 1.1
581      */

582     public boolean loadDataView(DataView dataView) throws IOException JavaDoc {
583         return loadDataView(dataView, Configuration.ACCEPT_ALL_DATAVIEWS);
584     }
585
586     /**
587      * @since 1.1
588      */

589     public boolean loadDataView(DataView dataView, Predicate dataViewNameFilter)
590             throws IOException JavaDoc {
591
592         Validate.notNull(dataView, "DataView cannot be null.");
593
594         if (dataViewLocations.size() == 0 || dataViewLocations.size() > 512) {
595             return false;
596         }
597
598         if (dataViewNameFilter == null)
599             dataViewNameFilter = Configuration.ACCEPT_ALL_DATAVIEWS;
600
601         List JavaDoc viewXMLSources = new ArrayList JavaDoc(dataViewLocations.size());
602         int index = 0;
603         for (Iterator JavaDoc i = dataViewLocations.entrySet().iterator(); i.hasNext(); index++) {
604             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
605             String JavaDoc name = (String JavaDoc) entry.getKey();
606             if (!dataViewNameFilter.evaluate(name))
607                 continue;
608             String JavaDoc location = (String JavaDoc) entry.getValue();
609             InputStream JavaDoc in = getViewConfiguration(location);
610             if (in != null)
611                 viewXMLSources.add(in);
612         }
613
614         if (viewXMLSources.isEmpty())
615             return false;
616
617         dataView.load((InputStream JavaDoc[]) viewXMLSources
618                 .toArray(new InputStream JavaDoc[viewXMLSources.size()]));
619         return true;
620     }
621
622     /**
623      * Shutdowns all owned domains. Invokes DataDomain.shutdown().
624      */

625     public void shutdown() {
626         Collection JavaDoc domains = getDomains();
627         for (Iterator JavaDoc i = domains.iterator(); i.hasNext();) {
628             DataDomain domain = (DataDomain) i.next();
629             domain.shutdown();
630         }
631     }
632
633     private class ConfigurationShutdownHook extends Thread JavaDoc {
634
635         public void run() {
636             shutdown();
637         }
638     }
639
640     public void installConfigurationShutdownHook() {
641         uninstallConfigurationShutdownHook();
642         Runtime.getRuntime().addShutdownHook(configurationShutdownHook);
643     }
644
645     public void uninstallConfigurationShutdownHook() {
646         Runtime.getRuntime().removeShutdownHook(configurationShutdownHook);
647     }
648     
649     /**
650      * Returns ClassLoader set for this instance if it is not null. Otherwise returns
651      * static ClassLoader by calling "Configuration.getResourceLoader()".
652      *
653      * @since 1.2
654      */

655     public ClassLoader JavaDoc getClassLoader() {
656         return classLoader != null ? classLoader : Configuration.getResourceLoader();
657     }
658     
659     /**
660      * @since 1.2
661      */

662     public void setClassLoader(ClassLoader JavaDoc classLoader) {
663         this.classLoader = classLoader;
664     }
665 }
Popular Tags