001: /*
002: * Copyright 2005 Ralf Joachim
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: * $Id: Configuration.java 6907 2007-03-28 21:24:52Z rjoachim $
017: */
018: package org.castor.util;
019:
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.net.MalformedURLException;
025: import java.net.URL;
026: import java.util.ArrayList;
027: import java.util.List;
028: import java.util.Properties;
029: import java.util.StringTokenizer;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.exolab.castor.core.exceptions.CastorRuntimeException;
034:
035: /**
036: * Class to hold Castor configuration properties.
037: *
038: * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
039: * @version $Revision: 6907 $ $Date: 2006-03-21 12:26:52 -0700 (Tue, 21 Mar 2006) $
040: * @since 1.0
041: */
042: public final class Configuration {
043: //--------------------------------------------------------------------------
044:
045: /** The <a href="http://jakarta.apache.org/commons/logging/">Jakarta
046: * Commons Logging</a> instance used for all logging. */
047: private static final Log LOG = LogFactory
048: .getLog(Configuration.class);
049:
050: /** Path to default configuration in Castor JAR. */
051: private static final String FILEPATH = "/org/exolab/castor/";
052:
053: /** Name of Castor configuration file. */
054: private static final String FILENAME = "castor.properties";
055:
056: /** The configuration instance. */
057: private static Configuration _config = null;
058:
059: /** The configured properties. */
060: private Properties _props = new Properties();
061:
062: //--------------------------------------------------------------------------
063:
064: /**
065: * Get the one and only configuration instance. If not done yet, a new configuration
066: * will be constructed and default properties will be loaded.
067: *
068: * @return The configuration instance.
069: */
070: public static Configuration getInstance() {
071: if (_config == null) {
072: _config = new Configuration();
073: }
074: return _config;
075: }
076:
077: /**
078: * Dispose the one and only configuration instance.
079: */
080: public static void disposeConfiguration() {
081: _config = null;
082: }
083:
084: //--------------------------------------------------------------------------
085:
086: /**
087: * Create a new Configuration instance loading properties from default location.
088: */
089: public Configuration() {
090: loadDefaultProperties();
091: }
092:
093: /**
094: * Create a new Configuration instance loading properties from given location.
095: *
096: * @param filename Absolute or relative filename of the properties file.
097: */
098: public Configuration(final String filename) {
099: loadProperties(filename);
100: }
101:
102: //--------------------------------------------------------------------------
103:
104: /**
105: * Load properties from default location.
106: */
107: public void loadDefaultProperties() {
108: boolean found = false;
109:
110: // Get detault configuration from the Castor JAR.
111: InputStream resourceStream = null;
112: try {
113: resourceStream = getClass().getResourceAsStream(
114: FILEPATH + FILENAME);
115: _props.load(resourceStream);
116: found = true;
117: } catch (Exception ex) {
118: // As we will be trying something else later, record the error for setup
119: // purposes, but do not actually treat as a critical failure.
120: LOG
121: .warn(
122: "Non-critical error during Castor configuration load:",
123: ex);
124: } finally {
125: if (resourceStream != null) {
126: try {
127: resourceStream.close();
128: } catch (IOException e) {
129: LOG.warn("Problem closing stream for " + FILEPATH
130: + FILENAME);
131: }
132: }
133: }
134:
135: // Get overriding configuration from the Java library directory, ignore if not
136: // found. If found merge existing properties.
137: try {
138: String javaHome = System.getProperty("java.home");
139: if (javaHome != null) {
140: File file = new File(new File(javaHome, "lib"),
141: FILENAME);
142: if (file.exists()) {
143: _props.load(new FileInputStream(file));
144: found = true;
145: }
146: }
147: } catch (SecurityException ex) {
148: // Not a critical error, but users will need to know if they need to change
149: // their config.
150: LOG
151: .warn(
152: "Security policy prevented access to Castor configuration:",
153: ex);
154: } catch (Exception ex) {
155: // As we will be trying something else later, record the error for setup
156: // purposes, but do not actually treat as a critical failure.
157: LOG
158: .warn(
159: "Non-critical error during Castor configuration load:",
160: ex);
161: }
162:
163: // Cannot find any castor.properties file(s).
164: if (!found) {
165: String msg = "Could not obtain the default configuration file '"
166: + FILENAME + "' from the Castor JAR.";
167: LOG.error(msg);
168: throw new RuntimeException(msg);
169: }
170:
171: // Get overriding configuration from the classpath, ignore if not found. If
172: // found, merge any existing properties.
173: InputStream classPathStream = null;
174: try {
175: URL url = getClass().getResource("/" + FILENAME);
176: if (url != null) {
177: classPathStream = url.openStream();
178: _props.load(classPathStream);
179: return;
180: }
181: } catch (Exception ex) {
182: // As we have previously loaded default configuration treat as a critical
183: // failure.
184: LOG
185: .warn(
186: "Non-critical error during Castor configuration load:",
187: ex);
188: } finally {
189: if (classPathStream != null) {
190: try {
191: classPathStream.close();
192: } catch (IOException e) {
193: LOG.warn("Problem closing stream for /" + FILENAME);
194: }
195: }
196: }
197:
198: // If not found, either it doesn't exist, or "." is not part of the class path,
199: // try looking at local working directory.
200: InputStream fileStream = null;
201: try {
202: File file = new File(FILENAME);
203: if (file.exists() && file.canRead()) {
204: fileStream = new FileInputStream(file);
205: _props.load(fileStream);
206: }
207: } catch (Exception ex) {
208: // As we have previously loaded default configuration treat as a critical
209: // failure.
210: LOG
211: .warn(
212: "Non-critical error during Castor configuration load:",
213: ex);
214: } finally {
215: if (fileStream != null) {
216: try {
217: fileStream.close();
218: } catch (IOException e) {
219: LOG.warn("Problem closing stream for " + FILENAME);
220: }
221: }
222: }
223: }
224:
225: /**
226: * Load properties from given filename. It first tries to interpret filename as
227: * absolute resource location. If this fails it's assumed that filename should be
228: * interpreted relative to classpath.
229: *
230: * @param filename Absolute or relative filename of the properties file.
231: */
232: public void loadProperties(final String filename) {
233: URL url = null;
234: try {
235: try {
236: url = new URL(filename);
237: } catch (MalformedURLException ex) {
238: url = getClass().getClassLoader().getResource(filename);
239: }
240: if (url != null) {
241: _props.load(url.openStream());
242: } else {
243: String msg = "Could not obtain the configuration file '"
244: + filename + "' from the Castor JAR.";
245: LOG.error(msg);
246: throw new RuntimeException(msg);
247: }
248: } catch (IOException ex) {
249: String msg = "Could not read the configuration file '"
250: + url.toExternalForm() + "' from the Castor JAR.";
251: LOG.error(msg, ex);
252: throw new CastorRuntimeException(msg, ex);
253: }
254: }
255:
256: /**
257: * Get the configured properties.
258: *
259: * @return The configured properties.
260: */
261: public Properties getProperties() {
262: return _props;
263: }
264:
265: /**
266: * Get property with given name as string or if property is not available return
267: * the given default string.
268: *
269: * @param name Name of the property.
270: * @param defaultValue Default string to return if property is not available.
271: * @return The configured string property or the default string if property is
272: * not available.
273: */
274: public String getProperty(final String name,
275: final String defaultValue) {
276: String value = _props.getProperty(name);
277: return (value == null) ? defaultValue : value;
278: }
279:
280: /**
281: * Get property with given name as string array or if property is not available
282: * return an empty string array.
283: *
284: * @param name Name of the property.
285: * @return The string array of configured property.
286: */
287: public String[] getProperty(final String name) {
288: String value = _props.getProperty(name);
289: if (value == null) {
290: return new String[] {};
291: }
292:
293: List array = new ArrayList();
294: StringTokenizer tokenizer = new StringTokenizer(value, ",");
295: while (tokenizer.hasMoreTokens()) {
296: array.add(tokenizer.nextToken().trim());
297: }
298: return (String[]) array.toArray(new String[array.size()]);
299: }
300:
301: /**
302: * Get property with given name as int value. If property is not available or can
303: * not be interpreted as integer the given default int value will be returned.
304: *
305: * @param name Name of the property.
306: * @param defaultValue Default int value to return if property is not available or
307: * can not be interpreted as integer.
308: * @return The configured int property or the default int value if property is
309: * not available or can not be interpreted as integer.
310: */
311: public int getProperty(final String name, final int defaultValue) {
312: String value = _props.getProperty(name);
313: try {
314: return Integer.parseInt(value);
315: } catch (NumberFormatException ex) {
316: return defaultValue;
317: }
318: }
319:
320: /**
321: * Get property with given name as boolean value. If property is not available or
322: * does not equal 'true' or 'false' the given default boolean value will be returned.
323: *
324: * @param name Name of the property.
325: * @param defaultValue Default boolean value to return if property is not available
326: * or does not equal 'true' or 'false'
327: * @return The configured boolean property or the default boolean value if property
328: * is not available or does not equal 'true' or 'false'.
329: */
330: public boolean getProperty(final String name,
331: final boolean defaultValue) {
332: String value = _props.getProperty(name);
333: if ("true".equalsIgnoreCase(value)) {
334: return true;
335: } else if ("false".equalsIgnoreCase(value)) {
336: return false;
337: } else {
338: return defaultValue;
339: }
340: }
341:
342: //--------------------------------------------------------------------------
343: }
|