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) | |
|---|---|
|
|
| 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 | */ |
| 19 | package org.topazproject.ambra.configuration; |
| 20 | |
| 21 | import java.net.URL; |
| 22 | import java.net.MalformedURLException; |
| 23 | import java.util.Enumeration; |
| 24 | import java.io.IOException; |
| 25 | import java.io.FileNotFoundException; |
| 26 | |
| 27 | import org.apache.commons.configuration.AbstractConfiguration; |
| 28 | import org.apache.commons.configuration.Configuration; |
| 29 | import org.apache.commons.configuration.SystemConfiguration; |
| 30 | import org.apache.commons.configuration.CombinedConfiguration; |
| 31 | import org.apache.commons.configuration.PropertiesConfiguration; |
| 32 | import org.apache.commons.configuration.XMLConfiguration; |
| 33 | import org.apache.commons.configuration.ConfigurationException; |
| 34 | import org.apache.commons.configuration.ConfigurationUtils; |
| 35 | import org.apache.commons.configuration.tree.OverrideCombiner; |
| 36 | import org.apache.commons.configuration.tree.UnionCombiner; |
| 37 | |
| 38 | import org.apache.commons.logging.Log; |
| 39 | import 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 | */ |
| 63 | public 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.
