All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.objectstyle.cayenne.conf.Configuration Maven / Gradle / Ivy

There is a newer version: 1.2.4
Show newest version
/* ====================================================================
 *
 * The ObjectStyle Group Software License, version 1.1
 * ObjectStyle Group - http://objectstyle.org/
 * 
 * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
 * of the software. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The end-user documentation included with the redistribution, if any,
 *    must include the following acknowlegement:
 *    "This product includes software developed by independent contributors
 *    and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 * 
 * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
 *    or promote products derived from this software without prior written
 *    permission. For written permission, email
 *    "andrus at objectstyle dot org".
 * 
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
 *    names without prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by many
 * individuals and hosted on ObjectStyle Group web site.  For more
 * information on the ObjectStyle Group, please see
 * .
 */
package org.objectstyle.cayenne.conf;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.Predicate;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.objectstyle.cayenne.CayenneRuntimeException;
import org.objectstyle.cayenne.ConfigurationException;
import org.objectstyle.cayenne.access.DataDomain;
import org.objectstyle.cayenne.dataview.DataView;
import org.objectstyle.cayenne.event.EventManager;
import org.objectstyle.cayenne.util.CayenneMap;
import org.objectstyle.cayenne.util.ResourceLocator;

/**
 * This class is an entry point to Cayenne. It loads all configuration files and
 * instantiates main Cayenne objects. Used as a singleton via the
 * {@link #getSharedConfiguration}method.
 * 

* To use a custom subclass of Configuration, Java applications must call * {@link #initializeSharedConfiguration}with the subclass as argument. This will create * and initialize a Configuration singleton instance of the specified class. By default * {@link DefaultConfiguration}is instantiated. *

* * @author Andrei Adamchik * @author Holger Hoffstaette */ public abstract class Configuration { private static Logger logObj = Logger.getLogger(Configuration.class); public static final String DEFAULT_LOGGING_PROPS_FILE = ".cayenne/cayenne-log.properties"; public static final String DEFAULT_DOMAIN_FILE = "cayenne.xml"; public static final Class DEFAULT_CONFIGURATION_CLASS = DefaultConfiguration.class; protected static Configuration sharedConfiguration = null; private static boolean loggingConfigured = false; public static final Predicate ACCEPT_ALL_DATAVIEWS = new Predicate() { public boolean evaluate(Object dataViewName) { return true; } }; /** * Lookup map that stores DataDomains with names as keys. */ protected CayenneMap dataDomains = new CayenneMap(this); protected DataSourceFactory overrideFactory; protected ConfigStatus loadStatus = new ConfigStatus(); protected String domainConfigurationName = DEFAULT_DOMAIN_FILE; protected boolean ignoringLoadFailures; protected ConfigLoaderDelegate loaderDelegate; protected ConfigSaverDelegate saverDelegate; protected ConfigurationShutdownHook configurationShutdownHook = new ConfigurationShutdownHook(); protected Map dataViewLocations = new HashMap(); protected String projectVersion; /** * @since 1.2 */ protected EventManager eventManager; /** * @deprecated since 1.2. Use Thread.currentThread().setContextClassLoader() instead. */ public static void bootstrapSharedConfiguration(Class cl) { logObj.warn("This method does nothing."); } /** * Configures Cayenne logging properties. Search for the properties file called * cayenne-log.properties is first done in $HOME/.cayenne, then in * CLASSPATH. */ public synchronized static void configureCommonLogging() { if (!Configuration.isLoggingConfigured()) { // create a simple CLASSPATH/$HOME locator ResourceLocator locator = new ResourceLocator(); locator.setSkipAbsolutePath(true); locator.setSkipClasspath(false); locator.setSkipCurrentDirectory(true); locator.setSkipHomeDirectory(false); // and load the default logging config file URL configURL = locator.findResource(DEFAULT_LOGGING_PROPS_FILE); Configuration.configureCommonLogging(configURL); } } /** * Configures Cayenne logging properties using properties found at the specified URL. */ public synchronized static void configureCommonLogging(URL propsFile) { if (!Configuration.isLoggingConfigured()) { if (propsFile != null) { PropertyConfigurator.configure(propsFile); logObj.debug("configured log4j from: " + propsFile); } else { BasicConfigurator.configure(); logObj.debug("configured log4j with BasicConfigurator."); } // remember configuration success Configuration.setLoggingConfigured(true); } } /** * Indicates whether Log4j has been initialized, either by cayenne or otherwise. If an * external setup has been detected, {@link #setLoggingConfigured}will be called to * remember this. */ public static boolean isLoggingConfigured() { if (!loggingConfigured) { // check for existing log4j setup if (Logger.getRootLogger().getAllAppenders().hasMoreElements()) { Configuration.setLoggingConfigured(true); } } return loggingConfigured; } /** * Indicate whether Log4j has been initialized. Can be used when subclasses customize * the initialization process, or to configure Log4J outside of Cayenne. */ public synchronized static void setLoggingConfigured(boolean state) { loggingConfigured = state; } /** * Use this method as an entry point to all Cayenne access objects. *

* Note that if you want to provide a custom Configuration, make sure you call one of * the {@link #initializeSharedConfiguration}methods before your application code has * a chance to call this method. */ public synchronized static Configuration getSharedConfiguration() { if (Configuration.sharedConfiguration == null) { Configuration.initializeSharedConfiguration(); } return Configuration.sharedConfiguration; } /** * @deprecated since 1.2 use Thread.currentThread().getContextClassLoader(). This is * what Cayenne uses internally. */ public static ClassLoader getResourceLoader() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = Configuration.class.getClassLoader(); } return loader; } /** * Returns default log level for loading configuration. Log level is made static so * that applications can set it before shared Configuration object is instantiated. * * @deprecated since 1.2 unused */ public static Level getLoggingLevel() { Level l = logObj.getLevel(); return (l != null ? l : Level.DEBUG); } /** * Sets the default log level for loading a configuration. * * @deprecated since 1.2 unused. */ public static void setLoggingLevel(Level logLevel) { logObj.setLevel(logLevel); } /** * Returns EventManager used by this configuration. * * @since 1.2 */ public EventManager getEventManager() { return eventManager; } /** * Sets EventManager used by this configuration. * * @since 1.2 */ public void setEventManager(EventManager eventManager) { this.eventManager = eventManager; } /** * Creates and initializes shared Configuration object. By default * {@link DefaultConfiguration}will be instantiated and assigned to a singleton * instance of Configuration. */ public static void initializeSharedConfiguration() { Configuration.initializeSharedConfiguration(DEFAULT_CONFIGURATION_CLASS); } /** * Creates and initializes a shared Configuration object of a custom Configuration * subclass. */ public static void initializeSharedConfiguration(Class configurationClass) { Configuration conf = null; try { conf = (Configuration) configurationClass.newInstance(); } catch (Exception ex) { logObj.error("Error creating shared Configuration: ", ex); throw new ConfigurationException("Error creating shared Configuration." + ex.getMessage(), ex); } Configuration.initializeSharedConfiguration(conf); } /** * Sets the shared Configuration object to a new Configuration object. First calls * {@link #canInitialize}and - if permitted -{@link #initialize}followed by * {@link #didInitialize}. */ public static void initializeSharedConfiguration(Configuration conf) { // check to see whether we can proceed if (!conf.canInitialize()) { throw new ConfigurationException("Configuration of class " + conf.getClass().getName() + " refused to be initialized."); } try { // initialize configuration conf.initialize(); // call post-initialization hook conf.didInitialize(); // set the initialized Configuration only after success Configuration.sharedConfiguration = conf; } catch (Exception ex) { throw new ConfigurationException( "Error during Configuration initialization. " + ex.getMessage(), ex); } } /** * Default constructor for new Configuration instances. Simply calls * {@link Configuration#Configuration(String)}. * * @see Configuration#Configuration(String) */ protected Configuration() { this(DEFAULT_DOMAIN_FILE); } /** * Default constructor for new Configuration instances using the given resource name * as the main domain file. First calls {@link #configureLogging}, then * {@link #setDomainConfigurationName}with the given domain configuration resource * name. */ protected Configuration(String domainConfigurationName) { // set up logging this.configureLogging(); // set domain configuration name this.setDomainConfigurationName(domainConfigurationName); this.eventManager = new EventManager(); } /** * Indicates whether {@link #initialize}can be called. Returning false * allows new instances to delay or refuse the initialization process. */ public abstract boolean canInitialize(); /** * Initializes the new instance. * * @throws Exception */ public abstract void initialize() throws Exception; /** * Called after successful completion of {@link #initialize}. */ public abstract void didInitialize(); /** * Returns the resource locator used for finding and loading resources. */ protected abstract ResourceLocator getResourceLocator(); /** * Returns a DataDomain as a stream or null if it cannot be found. */ // TODO: this method is only used in sublcass (DefaultConfiguration), // should we remove it from here? protected abstract InputStream getDomainConfiguration(); /** * Returns a DataMap with the given name or null if it cannot be found. */ protected abstract InputStream getMapConfiguration(String name); protected abstract InputStream getViewConfiguration(String location); /** * Configures log4J. This implementation calls * {@link Configuration#configureCommonLogging}. */ protected void configureLogging() { Configuration.configureCommonLogging(); } /** * Returns the name of the main domain configuration resource. Defaults to * {@link Configuration#DEFAULT_DOMAIN_FILE}. */ public String getDomainConfigurationName() { return this.domainConfigurationName; } /** * Sets the name of the main domain configuration resource. * * @param domainConfigurationName the name of the resource that contains this * Configuration's domain(s). */ protected void setDomainConfigurationName(String domainConfigurationName) { this.domainConfigurationName = domainConfigurationName; } /** * @since 1.1 */ public String getProjectVersion() { return projectVersion; } /** * @since 1.1 */ public void setProjectVersion(String projectVersion) { this.projectVersion = projectVersion; } /** * Returns an internal property for the DataSource factory that will override any * settings configured in XML. Subclasses may override this method to provide a * special factory for DataSource creation that will take precedence over any * factories configured in a cayenne project. */ public DataSourceFactory getDataSourceFactory() { return this.overrideFactory; } public void setDataSourceFactory(DataSourceFactory overrideFactory) { this.overrideFactory = overrideFactory; } /** * Adds new DataDomain to the list of registered domains. Injects EventManager used by * this configuration into the domain. */ public void addDomain(DataDomain domain) { this.dataDomains.put(domain.getName(), domain); // inject EventManager if (domain != null) { domain.setEventManager(getEventManager()); } logObj.debug("added domain: " + domain.getName()); } /** * Returns registered domain matching name or null if no * such domain is found. */ public DataDomain getDomain(String name) { return (DataDomain) this.dataDomains.get(name); } /** * Returns default domain of this configuration. If no domains are configured, * null is returned. If more than one domain exists in this * configuration, a CayenneRuntimeException is thrown, indicating that the domain name * must be explicitly specified. In such cases {@link #getDomain(String name)}must be * used instead. */ public DataDomain getDomain() { int size = this.dataDomains.size(); if (size == 0) { return null; } else if (size == 1) { return (DataDomain) this.dataDomains.values().iterator().next(); } else { throw new CayenneRuntimeException( "More than one domain is configured; use 'getDomain(String name)' instead."); } } /** * Unregisters DataDomain matching name from * this Configuration object. Note that any domain database * connections remain open, and it is a responsibility of a * caller to clean it up. */ public void removeDomain(String name) { DataDomain domain = (DataDomain) dataDomains.remove(name); if (domain != null) { domain.setEventManager(null); } logObj.debug("removed domain: " + name); } /** * Returns an unmodifiable collection of registered {@link DataDomain}objects. */ public Collection getDomains() { return Collections.unmodifiableCollection(dataDomains.values()); } /** * Returns whether to ignore any failures during map loading or not. * * @return boolean */ public boolean isIgnoringLoadFailures() { return this.ignoringLoadFailures; } /** * Sets whether to ignore any failures during map loading or not. * * @param ignoringLoadFailures true or false */ protected void setIgnoringLoadFailures(boolean ignoringLoadFailures) { this.ignoringLoadFailures = ignoringLoadFailures; } /** * Returns the load status. * * @return ConfigStatus */ public ConfigStatus getLoadStatus() { return this.loadStatus; } /** * Sets the load status. */ protected void setLoadStatus(ConfigStatus status) { this.loadStatus = status; } /** * Returns a delegate used for controlling the loading of configuration elements. */ public ConfigLoaderDelegate getLoaderDelegate() { return loaderDelegate; } /** * @since 1.1 */ public void setLoaderDelegate(ConfigLoaderDelegate loaderDelegate) { this.loaderDelegate = loaderDelegate; } /** * @since 1.2 */ public ConfigSaverDelegate getSaverDelegate() { return saverDelegate; } /** * @since 1.2 */ public void setSaverDelegate(ConfigSaverDelegate saverDelegate) { this.saverDelegate = saverDelegate; } /** * Initializes configuration with the location of data views. * * @since 1.1 * @param dataViewLocations Map of DataView locations. */ public void setDataViewLocations(Map dataViewLocations) { if (dataViewLocations == null) this.dataViewLocations = new HashMap(); else this.dataViewLocations = dataViewLocations; } /** * @since 1.1 */ public Map getDataViewLocations() { return dataViewLocations; } /** * @since 1.1 */ public boolean loadDataView(DataView dataView) throws IOException { return loadDataView(dataView, Configuration.ACCEPT_ALL_DATAVIEWS); } /** * @since 1.1 */ public boolean loadDataView(DataView dataView, Predicate dataViewNameFilter) throws IOException { if (dataView == null) { throw new IllegalArgumentException("DataView cannot be null."); } if (dataViewLocations.size() == 0 || dataViewLocations.size() > 512) { return false; } if (dataViewNameFilter == null) dataViewNameFilter = Configuration.ACCEPT_ALL_DATAVIEWS; List viewXMLSources = new ArrayList(dataViewLocations.size()); int index = 0; for (Iterator i = dataViewLocations.entrySet().iterator(); i.hasNext(); index++) { Map.Entry entry = (Map.Entry) i.next(); String name = (String) entry.getKey(); if (!dataViewNameFilter.evaluate(name)) continue; String location = (String) entry.getValue(); InputStream in = getViewConfiguration(location); if (in != null) viewXMLSources.add(in); } if (viewXMLSources.isEmpty()) return false; dataView.load((InputStream[]) viewXMLSources .toArray(new InputStream[viewXMLSources.size()])); return true; } /** * Shutdowns all owned domains. Invokes DataDomain.shutdown(). */ public void shutdown() { Collection domains = getDomains(); for (Iterator i = domains.iterator(); i.hasNext();) { DataDomain domain = (DataDomain) i.next(); domain.shutdown(); } } private class ConfigurationShutdownHook extends Thread { public void run() { shutdown(); } } public void installConfigurationShutdownHook() { uninstallConfigurationShutdownHook(); Runtime.getRuntime().addShutdownHook(configurationShutdownHook); } public void uninstallConfigurationShutdownHook() { Runtime.getRuntime().removeShutdownHook(configurationShutdownHook); } }