root/head/ambra/libs/conf-helper/src/main/java/org/topazproject/ambra/configuration/ConfigurationStore.java @ 7756

Revision 7756, 9.5 KB (checked in by dragisak, 15 months ago)

Add more logging: log name of each config file loaded.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id HeadURL Revision
Line 
1/* $HeadURL::                                                                            $
2 * $Id$
3 *
4 * Copyright (c) 2006-2009 by Topaz, Inc.
5 * http://topazproject.org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.topazproject.ambra.configuration;
20
21import java.net.URL;
22import java.net.MalformedURLException;
23import java.util.Enumeration;
24import java.io.IOException;
25import java.io.FileNotFoundException;
26
27import org.apache.commons.configuration.AbstractConfiguration;
28import org.apache.commons.configuration.Configuration;
29import org.apache.commons.configuration.SystemConfiguration;
30import org.apache.commons.configuration.CombinedConfiguration;
31import org.apache.commons.configuration.PropertiesConfiguration;
32import org.apache.commons.configuration.XMLConfiguration;
33import org.apache.commons.configuration.ConfigurationException;
34import org.apache.commons.configuration.ConfigurationUtils;
35import org.apache.commons.configuration.tree.OverrideCombiner;
36import org.apache.commons.configuration.tree.UnionCombiner;
37
38import org.apache.commons.logging.Log;
39import org.apache.commons.logging.LogFactory;
40
41/**
42 * A singleton that manages the load/unload/re-load of Configuration.<p>
43 *
44 * Configuration consists of a layered set of configuration files where configuration
45 * in a higher layer overrides those of the lower layers. Starting from the lowest layer,
46 * configuration consists of:
47 * <ul>
48 *   <li><var>/global-defaults.xml</var> - A resource in this library
49 *   <li><var>/defaults.xml</var> - A resource or resources in libraries and webapps using this lib
50 *   <li><var>ambra.configuration.overrides</var> - If set, this defines a named resource or URL
51 *        of a resource that is added to the configuration tree - usually supplementing
52 *        and overriding settings in <var>/global-defaults.xml</var> and <var>/defaults.xml</var>.
53 *   <li><var>file:/etc/topaz/ambra.xml</var> (or <var>ambra.configuration</var>) - A set of user
54 *        overrides in <var>/etc</var>. The name of this file can be changed for webapps that use
55 *        WebAppInitializer by changing web.xml or by setting the ambra.configuraiton system
56 *        property.
57 *   <li>System properties
58 * </ul>
59 *
60 * @author Pradeep Krishnan
61 * @author Eric Brown
62 */
63public class ConfigurationStore {
64  private static final Log                log       = LogFactory.getLog(ConfigurationStore.class);
65  private static final ConfigurationStore instance  = new ConfigurationStore();
66  private CombinedConfiguration           root = null;
67
68  /**
69   * A property used to define the location of the master set of configuration overrides.
70   * This is usually a xml or properties file in /etc somewhere. Note that this must be
71   * a URL. (For example: file:///etc/topaz/ambra.xml.)
72   */
73  public static final String CONFIG_URL = "ambra.configuration";
74
75  /**
76   * A property used to define overrides. This is primarily to support something like
77   * a development mode. If a valid URL, the resource is found from the URL. If not a
78   * URL, it is treated as the name of a resource.
79   */
80  public static final String OVERRIDES_URL = "ambra.configuration.overrides";
81
82  /**
83   * Default configuration overrides in /etc
84   */
85  public static final String DEFAULT_CONFIG_URL = "file:///etc/topaz/ambra.xml";
86
87  /**
88   * Name of resource(s) that contain defaults in a given library or web application.<p>
89   *
90   * Note that multiple copies of these may exist in the same classpath. ALL are read
91   * and added to the configuration. It is assumed that developers use appropriate
92   * namespaces to avoid collisions.<p>
93   *
94   * Also note that this does not begin with a / as ClassLoader.getResources() does
95   * not want this to begin with a /.
96   */
97  public static final String DEFAULTS_RESOURCE = "ambra/configuration/defaults.xml";
98
99  /**
100   * The name of the global defaults that exist in this library.<p>
101   *
102   * It is assumed there is only one of these in the classpath. If somebody defines
103   * a second copy of this, the results are undefined. (TODO: Detect this.)
104   */
105  public static final String GLOBAL_DEFAULTS_RESOURCE = "/ambra/configuration/global-defaults.xml";
106
107  /**
108   * Create the singleton instance.
109   */
110  private ConfigurationStore() {
111  }
112
113  /**
114   * Gets the singleton instance.
115   *
116   * @return Returns the only instance.
117   */
118  public static ConfigurationStore getInstance() {
119    return instance;
120  }
121
122  /**
123   * Gets the current configuration root.
124   *
125   * @return Returns the currently loaded configuration root
126   *
127   * @throws RuntimeException if the configuration factory is not initialized
128   */
129  public Configuration getConfiguration() {
130    if (root != null)
131      return root;
132
133    throw new RuntimeException("ERROR: Configuration not loaded or initialized.");
134  }
135
136  /**
137   * Overrides all existing configuration with the given conifguration object
138   * (useful for JUnit testing!)
139   * @param newConfig the new configuration to test
140   */
141  public void setConfiguration(CombinedConfiguration newConfig) {
142    root = newConfig;
143  }
144
145  /**
146   * Load/Reload the configuration from the factory config url.
147   *
148   * @param configURL URL to the config file for ConfigurationFactory
149   * @throws ConfigurationException when the config factory configuration has an error
150   */
151  public void loadConfiguration(URL configURL) throws ConfigurationException {
152    root = new CombinedConfiguration(new OverrideCombiner());
153
154    // System properties override everything
155    root.addConfiguration(new SystemConfiguration());
156
157    // Load from ambra.configuration -- /etc/... (optional)
158    if (configURL != null) {
159      try {
160        root.addConfiguration(getConfigurationFromUrl(configURL));
161        log.info("Added URL '" + configURL + "'");
162      } catch (ConfigurationException ce) {
163        if (!(ce.getCause() instanceof FileNotFoundException))
164          throw ce;
165        log.info("Unable to open '" + configURL + "'");
166      }
167    }
168
169    // Add ambra.configuration.overrides (if defined)
170    String overrides = System.getProperty(OVERRIDES_URL);
171    if (overrides != null) {
172      try {
173        root.addConfiguration(getConfigurationFromUrl(new URL(overrides)));
174        log.info("Added override URL '" + overrides + "'");
175      } catch (MalformedURLException mue) {
176        // Must not be a URL, so it must be a resource
177        addResources(root, overrides);
178      }
179    }
180
181    // Add defaults.xml found in classpath
182    CombinedConfiguration defaults = new CombinedConfiguration(new UnionCombiner());
183    addResources(defaults, DEFAULTS_RESOURCE);
184    root.addConfiguration(defaults);
185
186    // Add global-defaults.xml (presumably found in this jar)
187    addResources(root, GLOBAL_DEFAULTS_RESOURCE);
188
189    if (log.isDebugEnabled())
190      log.debug("Configuration dump: " + System.getProperty("line.separator") +
191                ConfigurationUtils.toString(root));
192  }
193
194  /**
195   * Use the default commons configuration specified by this library.
196   *
197   * @throws ConfigurationException when the configuration can't be found.
198   */
199  public void loadDefaultConfiguration() throws ConfigurationException {
200    // Allow JVM level property to override everything else
201    String name = System.getProperty(CONFIG_URL);
202    if (name == null)
203      name = DEFAULT_CONFIG_URL;
204
205    try {
206      loadConfiguration(new URL(name));
207    } catch (MalformedURLException e) {
208      throw new ConfigurationException("Invalid value of '" + name + "' for '" + CONFIG_URL +
209                                       "'. Must be a valid URL.");
210    }
211  }
212
213  /**
214   * Unload the current configuration.
215   */
216  public void unloadConfiguration() {
217    root = null;
218  }
219
220  /**
221   * Given a URL, determine whether it represents properties or xml and load it as a
222   * commons-config Configuration instance.
223   */
224  private static AbstractConfiguration getConfigurationFromUrl(URL url)
225      throws ConfigurationException {
226    if (url.getFile().endsWith("properties"))
227      return new PropertiesConfiguration(url);
228    else
229      return new XMLConfiguration(url);
230  }
231
232  /**
233   * Iterate over all the resources of the given name and add them to our root
234   * configuration.
235   * @param root the root configuration to add to
236   * @param resource the resource to add
237   * @throws ConfigurationException on an error in adding the new config
238   */
239  public static void addResources(CombinedConfiguration root, String resource)
240      throws ConfigurationException {
241    Class<?> klass = ConfigurationStore.class;
242    if (resource.startsWith("/")) {
243      root.addConfiguration(getConfigurationFromUrl(klass.getResource(resource)));
244      log.info("Added resource '" + resource + "' to configuration");
245    } else {
246      try {
247        Enumeration<URL> rs = klass.getClassLoader().getResources(resource);
248        while (rs.hasMoreElements()) {
249          URL resourceUrl = rs.nextElement();
250          root.addConfiguration(getConfigurationFromUrl(resourceUrl));
251          log.info("Added resource '" + resourceUrl + "' from path '" + resource + "' to configuration");
252        }
253      } catch (IOException ioe) {
254        throw new Error("Unexpected error loading resources", ioe);
255      }
256    }
257  }
258}
Note: See TracBrowser for help on using the browser.