Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/objectstyle/cayenne/conf/Configuration.java


1   /* ====================================================================
2    *
3    * The ObjectStyle Group Software License, Version 1.0
4    *
5    * Copyright (c) 2002-2003 The ObjectStyle Group
6    * and individual authors of the software.  All rights reserved.
7    *
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   *
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer.
14   *
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in
17   *    the documentation and/or other materials provided with the
18   *    distribution.
19   *
20   * 3. The end-user documentation included with the redistribution, if
21   *    any, must include the following acknowlegement:
22   *       "This product includes software developed by the
23   *        ObjectStyle Group (http://objectstyle.org/)."
24   *    Alternately, this acknowlegement may appear in the software itself,
25   *    if and wherever such third-party acknowlegements normally appear.
26   *
27   * 4. The names "ObjectStyle Group" and "Cayenne"
28   *    must not be used to endorse or promote products derived
29   *    from this software without prior written permission. For written
30   *    permission, please contact andrus@objectstyle.org.
31   *
32   * 5. Products derived from this software may not be called "ObjectStyle"
33   *    nor may "ObjectStyle" appear in their names without prior written
34   *    permission of the ObjectStyle Group.
35   *
36   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39   * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47   * SUCH DAMAGE.
48   * ====================================================================
49   *
50   * This software consists of voluntary contributions made by many
51   * individuals on behalf of the ObjectStyle Group.  For more
52   * information on the ObjectStyle Group, please see
53   * <http://objectstyle.org/>.
54   *
55   */
56  package org.objectstyle.cayenne.conf;
57  
58  import java.io.InputStream;
59  import java.net.URL;
60  import java.util.Collection;
61  import java.util.Collections;
62  import java.util.Iterator;
63  
64  import org.apache.log4j.BasicConfigurator;
65  import org.apache.log4j.Level;
66  import org.apache.log4j.Logger;
67  import org.apache.log4j.PropertyConfigurator;
68  import org.objectstyle.cayenne.CayenneRuntimeException;
69  import org.objectstyle.cayenne.ConfigurationException;
70  import org.objectstyle.cayenne.access.DataDomain;
71  import org.objectstyle.cayenne.util.CayenneMap;
72  import org.objectstyle.cayenne.util.ResourceLocator;
73  
74  /**
75   * This class is an entry point to Cayenne. It loads all
76   * configuration files and instantiates main Cayenne objects. Used as a
77   * singleton via the {@link #getSharedConfiguration} method.
78   *
79   * <p>To use a custom subclass of Configuration, Java applications must
80   * call {@link #initializeSharedConfiguration} with the subclass as argument.
81   * This will create and initialize a Configuration singleton instance of the
82   * specified class. By default {@link DefaultConfiguration} is instantiated.
83   * </p>
84   *
85   * @author Andrei Adamchik
86   * @author Holger Hoffstaette
87   */
88  public abstract class Configuration {
89      private static Logger logObj = Logger.getLogger(Configuration.class);
90  
91      public static final String DEFAULT_LOGGING_PROPS_FILE = ".cayenne/cayenne-log.properties";
92      public static final String DEFAULT_DOMAIN_FILE = "cayenne.xml";
93      public static final Class DEFAULT_CONFIGURATION_CLASS = DefaultConfiguration.class;
94  
95      protected static Configuration sharedConfiguration = null;
96      private static boolean loggingConfigured = false;
97  
98      /**
99       * Defines a ClassLoader to use for resource lookup.
100      * Configuration objects that are using ClassLoaders
101      * to locate resources may need to be bootstrapped
102      * explicitly.
103      */
104   protected static ClassLoader resourceLoader = Configuration.class.getClassLoader();
105 
106     /** Lookup map that stores DataDomains with names as keys. */
107   protected CayenneMap dataDomains = new CayenneMap(this);
108   protected Collection dataDomainsRef = Collections.unmodifiableCollection(dataDomains.values());
109   protected DataSourceFactory overrideFactory;
110   protected ConfigStatus loadStatus = new ConfigStatus();
111   protected String domainConfigurationName = DEFAULT_DOMAIN_FILE;
112   protected boolean ignoringLoadFailures = false;
113     protected ConfigurationShutdownHook configurationShutdownHook = new ConfigurationShutdownHook();
114 
115   /**
116    * Sets <code>cl</code> class's ClassLoader to serve
117    * as shared configuration resource ClassLoader.
118    * If shared Configuration object does not use ClassLoader,
119    * this method call will have no effect on how resources are loaded.
120    */
121   public static void bootstrapSharedConfiguration(Class cl) {
122     resourceLoader = cl.getClassLoader();
123   }
124 
125     /**
126      * Configures Cayenne logging properties.
127      * Search for the properties file called <code>cayenne-log.properties</code>
128      * is first done in $HOME/.cayenne, then in CLASSPATH.
129      */
130     public synchronized static void configureCommonLogging() {
131         if (!Configuration.isLoggingConfigured()) {
132       // create a simple CLASSPATH/$HOME locator
133             ResourceLocator locator = new ResourceLocator();
134             locator.setSkipAbsolutePath(true);
135             locator.setSkipClasspath(false);
136             locator.setSkipCurrentDirectory(true);
137             locator.setSkipHomeDirectory(false);
138 
139             // and load the default logging config file
140             URL configURL = locator.findResource(DEFAULT_LOGGING_PROPS_FILE);
141       Configuration.configureCommonLogging(configURL);
142         }
143     }
144 
145     /**
146      * Configures Cayenne logging properties using properties found at the specified URL.
147      */
148     public synchronized static void configureCommonLogging(URL propsFile) {
149         if (!Configuration.isLoggingConfigured()) {
150             if (propsFile != null) {
151                 PropertyConfigurator.configure(propsFile);
152         logObj.debug("configured log4j from: " + propsFile);
153             } else {
154                 BasicConfigurator.configure();
155                 logObj.debug("configured log4j with BasicConfigurator.");
156             }
157 
158       // remember configuration success
159             Configuration.setLoggingConfigured(true);
160         }
161     }
162 
163   /**
164    * Indicates whether Log4j has been initialized, either by cayenne
165    * or otherwise. If an external setup has been detected,
166    * {@link #setLoggingConfigured} will be called to remember this.
167    */
168   public static boolean isLoggingConfigured() {
169     if (!loggingConfigured) {
170       // check for existing log4j setup
171       if (Logger.getRootLogger().getAllAppenders().hasMoreElements()) {
172         Configuration.setLoggingConfigured(true);
173       }
174     }
175 
176     return loggingConfigured;
177   }
178 
179   /**
180    * Indicate whether Log4j has been initialized. Can be used when
181    * subclasses customize the initialization process, or to configure
182      * Log4J outside of Cayenne.
183    */
184   public synchronized static void setLoggingConfigured(boolean state) {
185     loggingConfigured = state;
186   }
187 
188   /**
189    * Use this method as an entry point to all Cayenne access objects.
190    * <p>Note that if you want to provide a custom Configuration,
191    * make sure you call one of the {@link #initializeSharedConfiguration} methods
192    * before your application code has a chance to call this method.
193    */
194   public synchronized static Configuration getSharedConfiguration() {
195     if (Configuration.sharedConfiguration == null) {
196       Configuration.initializeSharedConfiguration();
197     }
198 
199     return Configuration.sharedConfiguration;
200   }
201 
202   /**
203    * Returns the ClassLoader used to load resources.
204    */
205     public static ClassLoader getResourceLoader() {
206         return Configuration.resourceLoader;
207     }
208 
209     /**
210      * Returns default log level for loading configuration.
211      * Log level is made static so that applications can set it
212      * before shared Configuration object is instantiated.
213      */
214     public static Level getLoggingLevel() {
215       Level l = logObj.getLevel();
216       return (l != null ? l : Level.DEBUG);
217     }
218 
219     /**
220      * Sets the default log level for loading a configuration.
221      */
222     public static void setLoggingLevel(Level logLevel) {
223     logObj.setLevel(logLevel);
224     }
225 
226   /**
227    * Creates and initializes shared Configuration object.
228    * By default {@link DefaultConfiguration} will be
229    * instantiated and assigned to a singleton instance of
230    * Configuration.
231    */
232   public static void initializeSharedConfiguration() {
233     Configuration.initializeSharedConfiguration(DEFAULT_CONFIGURATION_CLASS);
234   }
235 
236   /**
237    * Creates and initializes a shared Configuration object of a
238    * custom Configuration subclass.
239    */
240   public static void initializeSharedConfiguration(Class configurationClass) {
241     Configuration conf = null;
242 
243     try {
244       conf = (Configuration)configurationClass.newInstance();
245     } catch (Exception ex) {
246       logObj.error("Error creating shared Configuration: ", ex);
247       throw new ConfigurationException("Error creating shared Configuration." + ex.getMessage(), ex);
248     }
249 
250     Configuration.initializeSharedConfiguration(conf);
251   }
252 
253   /**
254    * Sets the shared Configuration object to a new Configuration object.
255    * First calls {@link #canInitialize} and - if permitted -
256    * {@link #initialize} followed by {@link #didInitialize}.
257    */
258   public static void initializeSharedConfiguration(Configuration conf) {
259     // check to see whether we can proceed
260     if (!conf.canInitialize()) {
261       throw new ConfigurationException("Configuration of class "
262                 + conf.getClass().getName()
263                 + " refused to be initialized.");
264     }
265 
266     try {
267       // initialize configuration
268       conf.initialize();
269 
270       // call post-initialization hook
271       conf.didInitialize();
272 
273       // set the initialized Configuration only after success
274       Configuration.sharedConfiguration = conf;
275     } catch (Exception ex) {
276       throw new ConfigurationException("Error during Configuration initialization. " + ex.getMessage(), ex);
277     }
278   }
279 
280   /**
281    * Default constructor for new Configuration instances.
282    * Simply calls {@link Configuration#Configuration(String)}.
283    * @see Configuration#Configuration(String)
284    */
285   protected Configuration() {
286     this(DEFAULT_DOMAIN_FILE);
287   }
288 
289   /**
290    * Default constructor for new Configuration instances using the
291    * given resource name as the main domain file.
292    * First calls {@link #configureLogging}, then {@link #setDomainConfigurationName}
293    * with the given domain configuration resource name.
294    */
295   protected Configuration(String domainConfigurationName) {
296     super();
297 
298     // set up logging
299     this.configureLogging();
300 
301     // set domain configuration name
302     this.setDomainConfigurationName(domainConfigurationName);
303   }
304 
305   /**
306    * Indicates whether {@link #initialize} can be called.
307    * Returning <code>false</code> allows new instances to delay
308    * or refuse the initialization process.
309    */
310   public abstract boolean canInitialize();
311 
312   /**
313    * Initializes the new instance.
314    * @throws Exception
315    */
316   public abstract void initialize() throws Exception;
317 
318   /**
319    * Called after successful completion of {@link #initialize}.
320    */
321   public abstract void didInitialize();
322 
323   /**
324    * Returns the resource locator used for finding and loading resources.
325    */
326   protected abstract ResourceLocator getResourceLocator();
327 
328   /**
329    * Returns a DataDomain as a stream or <code>null</code>
330    * if it cannot be found.
331    */
332   protected abstract InputStream getDomainConfiguration();
333 
334   /**
335    * Returns a DataMap with the given name or <code>null</code>
336    * if it cannot be found.
337    */
338   protected abstract InputStream getMapConfiguration(String name);
339 
340 
341     /**
342      * Configures log4J. This implementation calls
343      * {@link Configuration#configureCommonLogging}.
344      */
345     protected void configureLogging() {
346         Configuration.configureCommonLogging();
347     }
348 
349   /**
350    * Returns the name of the main domain configuration resource.
351    * Defaults to {@link Configuration#DEFAULT_DOMAIN_FILE}.
352    */
353   public String getDomainConfigurationName() {
354     return this.domainConfigurationName;
355   }
356 
357   /**
358    * Sets the name of the main domain configuration resource.
359    * @param domainConfigurationName the name of the resource that contains
360    * this Configuration's domain(s).
361    */
362   protected void setDomainConfigurationName(String domainConfigurationName) {
363     this.domainConfigurationName = domainConfigurationName;
364   }
365 
366     /**
367      * Returns an internal property for the DataSource factory that
368      * will override any settings configured in XML.
369      * Subclasses may override this method to provide a special factory for
370      * DataSource creation that will take precedence over any factories
371      * configured in a cayenne project.
372      */
373     public DataSourceFactory getDataSourceFactory() {
374         return this.overrideFactory;
375     }
376 
377     public void setDataSourceFactory(DataSourceFactory overrideFactory) {
378         this.overrideFactory = overrideFactory;
379     }
380 
381     /**
382      * Adds new DataDomain to the list of registered domains.
383      */
384     public void addDomain(DataDomain domain) {
385         this.dataDomains.put(domain.getName(), domain);
386     logObj.debug("added domain: " + domain.getName());
387     }
388 
389     /**
390      * Returns registered domain matching <code>name</code>
391      * or <code>null</code> if no such domain is found.
392      */
393     public DataDomain getDomain(String name) {
394         return (DataDomain)this.dataDomains.get(name);
395     }
396 
397     /**
398      * Returns default domain of this configuration. If no domains are
399      * configured, <code>null</code> is returned. If more than one domain
400      * exists in this configuration, a CayenneRuntimeException is thrown,
401      * indicating that the domain name must be explicitly specified.
402      * In such cases {@link #getDomain(String name)} must be used instead.
403      */
404     public DataDomain getDomain() {
405         int size = this.dataDomains.size();
406         if (size == 0) {
407             return null;
408         } else if (size == 1) {
409             return (DataDomain)this.dataDomains.values().iterator().next();
410         } else {
411             throw new CayenneRuntimeException("More than one domain is configured; use 'getDomain(String name)' instead.");
412         }
413     }
414 
415     /**
416      * Unregisters DataDomain matching <code>name<code> from
417      * this Configuration object. Note that any domain database
418      * connections remain open, and it is a responsibility of a
419      * caller to clean it up.
420      */
421     public void removeDomain(String name) {
422         this.dataDomains.remove(name);
423     logObj.debug("removed domain: " + name);
424     }
425 
426   /**
427    * Returns an unmodifiable collection of registered {@link DataDomain} objects.
428    */
429   public Collection getDomains() {
430     return this.dataDomainsRef;
431   }
432 
433     /**
434      * Returns whether to ignore any failures during map loading or not.
435      * @return boolean
436      */
437     public boolean isIgnoringLoadFailures() {
438         return this.ignoringLoadFailures;
439     }
440 
441     /**
442      * Sets whether to ignore any failures during map loading or not.
443      * @param ignoringLoadFailures <code>true</code> or <code>false</code>
444      */
445     protected void setIgnoringLoadFailures(boolean ignoringLoadFailures) {
446         this.ignoringLoadFailures = ignoringLoadFailures;
447     }
448 
449   /**
450    * Returns the load status.
451    * @return ConfigStatus
452    */
453   public ConfigStatus getLoadStatus() {
454     return this.loadStatus;
455   }
456 
457   /**
458    * Sets the load status.
459    */
460   protected void setLoadStatus(ConfigStatus status) {
461     this.loadStatus = status;
462   }
463 
464   /**
465    * Returns a delegate used for controlling the loading of
466    * configuration elements.
467    * By default a {@link RuntimeLoadDelegate} is used.
468    */
469   public ConfigLoaderDelegate getLoaderDelegate() {
470     return new RuntimeLoadDelegate(this, this.getLoadStatus(), Configuration.getLoggingLevel());
471   }
472 
473     /**
474      * Shutdowns all owned domains. Invokes DataDomain.shutdown().
475      */
476     public void shutdown() {
477         Collection domains = getDomains();
478         for (Iterator i = domains.iterator(); i.hasNext(); ) {
479             DataDomain domain = (DataDomain)i.next();
480             domain.shutdown();
481         }
482     }
483 
484     private class ConfigurationShutdownHook extends Thread {
485         public void run() {
486             shutdown();
487         }
488     }
489 
490     public void installConfigurationShutdownHook() {
491         uninstallConfigurationShutdownHook();
492         Runtime.getRuntime().addShutdownHook(configurationShutdownHook);
493     }
494 
495     public void uninstallConfigurationShutdownHook() {
496         Runtime.getRuntime().removeShutdownHook(configurationShutdownHook);
497     }
498 }