001: /* Copyright 2004 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.properties;
007:
008: import java.util.Collections;
009: import java.util.HashSet;
010: import java.util.Properties;
011: import java.util.Set;
012:
013: import org.apache.commons.logging.Log;
014: import org.apache.commons.logging.LogFactory;
015:
016: /**
017: * Provides access to properties.
018: * <p>It is important to understand that usage of this class is different from what you might
019: * be used to in java.util.Properties. Specifically, when you get a Properties property,
020: * if that property is not set, the return value is NULL. However, when you call the basic getters here,
021: * if the property is not set, a RuntimeException is thrown. These methods will never return null (except
022: * if you pass in null as the default return value for the methods that take a default).</p>
023: * <p>There are methods to get properties as various primitive types, int, double, float, etc.
024: * When you invoke one of these methods on a property that is found but cannot be parsed as
025: * your desired type, a RuntimeException is thrown.</p>
026: * <p>There are
027: * corresponding methods which take as a second parameter a default value. These methods, instead of
028: * throwing a RuntimeException when the property cannot be found, return the default value. You can
029: * use the default value "null" to invoke getProperty() with semantics more like the java.util.Properties object.
030: * These augmented accessors which take defaults will be, I hope, especially useful in static initializers. Providing a
031: * default in your static initializer will keep your class from blowing up at initialization when your property cannot be found.
032: * This seems especially advantageous when there is a plausible default value.</p>
033: * <p>This class has a comprehensive JUnit testcase. Please keep the testcase up to date with any changes you make to this class.</p>
034: * @author Ken Weiner, kweiner@unicon.net
035: * @author howard.gilbert@yale.edu
036: * @author andrew.petro@yale.edu
037: * @version $Revision: 35627 $ $Date: 2005-04-18 18:19:28 -0700 (Mon, 18 Apr 2005) $
038: * @since uPortal 2.4, this class existed in the main package since uPortal 2.0
039: */
040: public class PropertiesManager {
041:
042: protected static final Log log = LogFactory
043: .getLog(PropertiesManager.class);
044:
045: public static final String PORTAL_PROPERTIES_FILE_SYSTEM_VARIABLE = "portal.properties";
046: private static final String PORTAL_PROPERTIES_FILE_NAME = "/properties/portal.properties";
047: private static Properties props = null;
048:
049: /**
050: * A set of the names of properties that clients of this class attempt to access
051: * but which were not set in the properties file.
052: * This Set allows this class to report about missing properties and to
053: * log each missing property only the first time it is requested.
054: */
055: private static final Set missingProperties = Collections
056: .synchronizedSet(new HashSet());
057:
058: /**
059: * Setter method to set the underlying Properties.
060: * This is a public method to allow poor-man's static dependency injection of the Properties from wherever you want to get them.
061: * If Properties have not been injected before any accessor method is invoked, PropertiesManager will invoke loadProperties() to attempt
062: * to load its own properties. You might call this from a context listener, say.
063: * If Properties have already been loaded or injected, this method will overwrite them.
064: * @param props - Properties to be injected.
065: */
066: public static synchronized void setProperties(Properties props) {
067: PropertiesManager.props = props;
068: }
069:
070: /**
071: * Load up the portal properties. Right now the portal properties is a simple
072: * .properties file with name value pairs. It may evolve to become an XML file
073: * later on.
074: */
075: protected static void loadProps() {
076: PropertiesManager.props = new Properties();
077: try {
078: String pfile = System
079: .getProperty(PORTAL_PROPERTIES_FILE_SYSTEM_VARIABLE);
080: if (pfile == null) {
081: pfile = PORTAL_PROPERTIES_FILE_NAME;
082: }
083: PropertiesManager.props.load(PropertiesManager.class
084: .getResourceAsStream(pfile));
085: } catch (Throwable t) {
086: log.error("Unable to read portal.properties file.", t);
087: }
088: }
089:
090: /**
091: * Returns the value of a property for a given name.
092: * Any whitespace is trimmed off the beginning and
093: * end of the property value.
094: * Note that this method will never return null.
095: * If the requested property cannot be found, this method throws an UndeclaredPortalException.
096: * @param name the name of the requested property
097: * @return value the value of the property matching the requested name
098: * @throws MissingPropertyException - if the requested property cannot be found
099: */
100: public static String getProperty(String name)
101: throws MissingPropertyException {
102: if (log.isTraceEnabled()) {
103: log.trace("entering getProperty(" + name + ")");
104: }
105: if (PropertiesManager.props == null)
106: loadProps();
107: String val = getPropertyUntrimmed(name);
108: val = val.trim();
109: if (log.isTraceEnabled()) {
110: log.trace("returning from getProperty(" + name
111: + ") with return value [" + val + "]");
112: }
113: return val;
114: }
115:
116: /**
117: * Returns the value of a property for a given name
118: * including whitespace trailing the property value, but not including
119: * whitespace leading the property value.
120: * An UndeclaredPortalException is thrown if the property cannot be found.
121: * This method will never return null.
122: * @param name the name of the requested property
123: * @return value the value of the property matching the requested name
124: * @throws MissingPropertyException - (undeclared) if the requested property is not found
125: */
126: public static String getPropertyUntrimmed(String name)
127: throws MissingPropertyException {
128: if (PropertiesManager.props == null)
129: loadProps();
130:
131: if (props == null) {
132: boolean alreadyReported = registerMissingProperty(name);
133: throw new MissingPropertyException(name, alreadyReported);
134: }
135:
136: String val = props.getProperty(name);
137: if (val == null) {
138: boolean alreadyReported = registerMissingProperty(name);
139: throw new MissingPropertyException(name, alreadyReported);
140: }
141: return val;
142: }
143:
144: /**
145: * Returns the value of a property for a given name.
146: * This method can be used if the property is boolean in
147: * nature and you want to make sure that <code>true</code> is
148: * returned if the property is set to "true", "yes", "y", or "on"
149: * (regardless of case),
150: * and <code>false</code> is returned in all other cases.
151: * @param name the name of the requested property
152: * @return value <code>true</code> if property is set to "true", "yes", "y", or "on" regardless of case, otherwise <code>false</code>
153: * @throws MissingPropertyException - when no property of the given name is declared.
154: */
155: public static boolean getPropertyAsBoolean(String name)
156: throws MissingPropertyException {
157: if (PropertiesManager.props == null)
158: loadProps();
159: boolean retValue = false;
160: String value = getProperty(name);
161: if (value != null) {
162: if (value.equalsIgnoreCase("true")
163: || value.equalsIgnoreCase("yes")
164: || value.equalsIgnoreCase("y")
165: || value.equalsIgnoreCase("on")) {
166: retValue = true;
167: } else if (value.equalsIgnoreCase("false")
168: || value.equalsIgnoreCase("no")
169: || value.equalsIgnoreCase("n")
170: || value.equalsIgnoreCase("off")) {
171: retValue = false;
172: } else {
173: // this method's historical behavior, maintained here, is to return false
174: // for all values that did not match on of the true values above.
175: log
176: .error("property ["
177: + name
178: + "] is being accessed as a boolean "
179: + "but had non-canonical value ["
180: + value
181: + "]. Returning it as false, "
182: + "but this may be a property misconfiguration.");
183: }
184: } else {
185: log
186: .fatal("property ["
187: + name
188: + "] is being accessed as a boolean "
189: + "but was null. Returning false. However, it should not have been "
190: + "possible to get here because getProperty() throws a runtime "
191: + "exception or returns a non-null value.");
192: }
193: return retValue;
194: }
195:
196: /**
197: * Returns the value of a property for a given name as a <code>byte</code>
198: * @param name the name of the requested property
199: * @return value the property's value as a <code>byte</code>
200: * @throws MissingPropertyException - if the property is not set
201: * @throws BadPropertyException - if the property cannot be parsed as a byte
202: */
203: public static byte getPropertyAsByte(String name)
204: throws MissingPropertyException, BadPropertyException {
205: if (PropertiesManager.props == null)
206: loadProps();
207: try {
208: return Byte.parseByte(getProperty(name));
209: } catch (NumberFormatException nfe) {
210: throw new BadPropertyException(name, getProperty(name),
211: "byte");
212: }
213:
214: }
215:
216: /**
217: * Returns the value of a property for a given name as a <code>short</code>
218: * @param name the name of the requested property
219: * @return value the property's value as a <code>short</code>
220: * @throws MissingPropertyException - if the property is not set
221: * @throws BadPropertyException - if the property cannot be parsed as a short or is not set.
222: */
223: public static short getPropertyAsShort(String name)
224: throws MissingPropertyException, BadPropertyException {
225: if (PropertiesManager.props == null)
226: loadProps();
227: try {
228: return Short.parseShort(getProperty(name));
229: } catch (NumberFormatException nfe) {
230: throw new BadPropertyException(name, getProperty(name),
231: "short");
232: }
233: }
234:
235: /**
236: * Returns the value of a property for a given name as an <code>int</code>
237: * @param name the name of the requested property
238: * @return value the property's value as an <code>int</code>
239: * @throws MissingPropertyException - if the property is not set
240: * @throws BadPropertyException - if the property cannot be parsed as an int
241: */
242: public static int getPropertyAsInt(String name)
243: throws MissingPropertyException, BadPropertyException {
244: if (PropertiesManager.props == null)
245: loadProps();
246: try {
247: return Integer.parseInt(getProperty(name));
248: } catch (NumberFormatException nfe) {
249: throw new BadPropertyException(name, getProperty(name),
250: "int");
251: }
252: }
253:
254: /**
255: * Returns the value of a property for a given name as a <code>long</code>
256: * @param name the name of the requested property
257: * @return value the property's value as a <code>long</code>
258: * @throws MissingPropertyException - if the property is not set
259: * @throws BadPropertyException - if the property cannot be parsed as a long
260: */
261: public static long getPropertyAsLong(String name)
262: throws MissingPropertyException, BadPropertyException {
263: if (PropertiesManager.props == null)
264: loadProps();
265: try {
266: return Long.parseLong(getProperty(name));
267: } catch (NumberFormatException nfe) {
268: throw new BadPropertyException(name, getProperty(name),
269: "long");
270: }
271: }
272:
273: /**
274: * Returns the value of a property for a given name as a <code>float</code>
275: * @param name the name of the requested property
276: * @return value the property's value as a <code>float</code>
277: * @throws MissingPropertyException - if the property is not set
278: * @throws BadPropertyException - if the property cannot be parsed as a float
279: */
280: public static float getPropertyAsFloat(String name)
281: throws MissingPropertyException, BadPropertyException {
282: if (PropertiesManager.props == null)
283: loadProps();
284: try {
285: return Float.parseFloat(getProperty(name));
286: } catch (NumberFormatException nfe) {
287: throw new BadPropertyException(name, getProperty(name),
288: "float");
289: }
290:
291: }
292:
293: /**
294: * Returns the value of a property for a given name as a <code>long</code>
295: * @param name the name of the requested property
296: * @return value the property's value as a <code>double</code>
297: * @throws MissingPropertyException - if the property has not been set
298: * @throws BadPropertyException - if the property cannot be parsed as a double or is not set.
299: */
300: public static double getPropertyAsDouble(String name)
301: throws MissingPropertyException, BadPropertyException {
302: if (PropertiesManager.props == null)
303: loadProps();
304: try {
305: return Double.parseDouble(getProperty(name));
306: } catch (NumberFormatException nfe) {
307: throw new BadPropertyException(name, getProperty(name),
308: "double");
309: }
310: }
311:
312: /**
313: * Registers that a given property was sought but not found.
314: * Currently adds the property to the set of missing properties and
315: * logs if this is the first time the property has been requested.
316: * @param name - the name of the missing property
317: * @return true if the property was previously registered, false otherwise
318: *
319: */
320: private static boolean registerMissingProperty(String name) {
321: final boolean previouslyReported = !PropertiesManager.missingProperties
322: .add(name);
323:
324: if (!previouslyReported && log.isInfoEnabled()) {
325: log.info("Property [" + name
326: + "] was requested but not found.");
327: }
328: return previouslyReported;
329: }
330:
331: /**
332: * Get the value of the property with the given name.
333: * If the named property is not found, returns the supplied default value.
334: * This error handling behavior makes this method attractive for use in static initializers.
335: * @param name - the name of the property to be retrieved.
336: * @param defaultValue - a fallback default value which will be returned if the property cannot be found.
337: * @return the value of the requested property, or the supplied default value if the named property cannot be found.
338: * @since uPortal 2.4
339: */
340: public static String getProperty(String name, String defaultValue) {
341: if (PropertiesManager.props == null)
342: loadProps();
343: String returnValue = defaultValue;
344: try {
345: returnValue = getProperty(name);
346: } catch (MissingPropertyException mpe) {
347: // Do nothing, since we have already recorded and logged the missing property.
348: }
349: return returnValue;
350: }
351:
352: /**
353: * Get the value of a property for the given name
354: * including any whitespace that may be at the beginning or end of the property value.
355: * This method returns the supplied default value if the requested property cannot be found.
356: * This error handling behavior makes this method attractive for use in static initializers.
357: * @param name - the name of the requested property
358: * @param defaultValue - a default value to fall back on if the property cannot be found
359: * @return the value of the property with the given name, or the supplied default value if the property could not be found.
360: * @since uPortal 2.4
361: */
362: public static String getPropertyUntrimmed(String name,
363: String defaultValue) {
364: if (PropertiesManager.props == null)
365: loadProps();
366: String returnValue = defaultValue;
367: try {
368: returnValue = getPropertyUntrimmed(name);
369: } catch (MissingPropertyException mpe) {
370: // do nothing, since we have already logged the missing property
371: }
372: return returnValue;
373:
374: }
375:
376: /**
377: * Get a property as a boolean, specifying a default value.
378: * If for any reason we are unable to lookup the desired property,
379: * this method returns the supplied default value.
380: * This error handling behavior makes this method suitable for calling from static initializers.
381: * @param name - the name of the property to be accessed
382: * @param defaultValue - default value that will be returned in the event of any error
383: * @return the looked up property value, or the defaultValue if any problem.
384: * @since uPortal 2.4
385: */
386: public static boolean getPropertyAsBoolean(final String name,
387: final boolean defaultValue) {
388: if (PropertiesManager.props == null)
389: loadProps();
390: boolean returnValue = defaultValue;
391: try {
392: returnValue = getPropertyAsBoolean(name);
393: } catch (MissingPropertyException mpe) {
394: // do nothing, since we already logged the missing property
395: }
396: return returnValue;
397: }
398:
399: /**
400: * Get the value of the given property as a byte, specifying a fallback default value.
401: * If for any reason we are unable to lookup the desired property,
402: * this method returns the supplied default value.
403: * This error handling behavior makes this method suitable for calling from static initializers.
404: * @param name - the name of the property to be accessed
405: * @param defaultValue - the default value that will be returned in the event of any error
406: * @return the looked up property value, or the defaultValue if any problem.
407: * @since uPortal 2.4
408: */
409: public static byte getPropertyAsByte(final String name,
410: final byte defaultValue) {
411: if (PropertiesManager.props == null)
412: loadProps();
413: byte returnValue = defaultValue;
414: try {
415: returnValue = getPropertyAsByte(name);
416: } catch (Throwable t) {
417: log.error("Could not retrieve or parse as byte property ["
418: + name + "], defaulting to [" + defaultValue + "]",
419: t);
420: }
421: return returnValue;
422: }
423:
424: /**
425: * Returns the value of a property for a given name as a short.
426: * If for any reason the property cannot be looked up as a short, returns the supplied default value.
427: * This error handling makes this method a good choice for static initializer calls.
428: * @param name - the name of the requested property
429: * @param defaultValue - a default value that will be returned in the event of any error
430: * @return the property value as a short or the default value in the event of any error
431: * @since uPortal 2.4
432: */
433: public static short getPropertyAsShort(String name,
434: short defaultValue) {
435: if (PropertiesManager.props == null)
436: loadProps();
437: short returnValue = defaultValue;
438: try {
439: returnValue = getPropertyAsShort(name);
440: } catch (Throwable t) {
441: log.error("Could not retrieve or parse as short property ["
442: + name + "], defaulting to given value ["
443: + defaultValue + "]", t);
444: }
445: return returnValue;
446: }
447:
448: /**
449: * Get the value of a given property as an int.
450: * If for any reason the property cannot be looked up as an int, returns the supplied default value.
451: * This error handling makes this method a good choice for static initializer calls.
452: * @param name - the name of the requested property
453: * @param defaultValue - a fallback default value for the property
454: * @return the value of the property as an int, or the supplied default value in the event of any problem.
455: * @since uPortal 2.4
456: */
457: public static int getPropertyAsInt(String name, int defaultValue) {
458: if (PropertiesManager.props == null)
459: loadProps();
460: int returnValue = defaultValue;
461: try {
462: returnValue = getPropertyAsInt(name);
463: } catch (Throwable t) {
464: log
465: .error(
466: "Could not retrieve or parse as int the property ["
467: + name + "], defaulting to "
468: + defaultValue, t);
469: }
470: return returnValue;
471: }
472:
473: /**
474: * Get the value of the given property as a long.
475: * If for any reason the property cannot be looked up as a long, returns the supplied default value.
476: * This error handling makes this method a good choice for static initializer calls.
477: * @param name - the name of the requested property
478: * @param defaultValue - a fallback default value that will be returned if there is any problem
479: * @return the value of the property as a long, or the supplied default value if there is any problem.
480: * @since uPortal 2.4
481: */
482: public static long getPropertyAsLong(String name, long defaultValue) {
483: if (PropertiesManager.props == null)
484: loadProps();
485: long returnValue = defaultValue;
486: try {
487: returnValue = getPropertyAsLong(name);
488: } catch (Throwable t) {
489: log.error("Could not retrieve or parse as long property ["
490: + name + "], defaulting to " + defaultValue, t);
491: }
492: return returnValue;
493: }
494:
495: /**
496: * Get the value of the given property as a float.
497: * If for any reason the property cannot be looked up as a float, returns the supplied default value.
498: * This error handling makes this method a good choice for static initializer calls.
499: * @param name - the name of the requested property
500: * @param defaultValue - a fallback default value that will be returned if there is any problem
501: * @return the value of the property as a float, or the supplied default value if there is any problem.
502: * @since uPortal 2.4
503: */
504: public static float getPropertyAsFloat(String name,
505: float defaultValue) {
506: if (PropertiesManager.props == null)
507: loadProps();
508: float returnValue = defaultValue;
509: try {
510: returnValue = getPropertyAsFloat(name);
511: } catch (Throwable t) {
512: log.error("Could not retrieve or parse as float property ["
513: + name + "], defaulting to " + defaultValue, t);
514: }
515: return returnValue;
516: }
517:
518: /**
519: * Get the value of the given property as a double.
520: * If for any reason the property cannot be looked up as a double, returns the specified default value.
521: * This error handling makes this method a good choice for static initializer calls.
522: * @param name - the name of the requested property
523: * @param defaultValue - a fallback default value that will be returned if there is any problem
524: * @return the value of the property as a double, or the supplied default value if there is any problem.
525: * @since uPortal 2.4
526: */
527: public static double getPropertyAsDouble(String name,
528: double defaultValue) {
529: if (PropertiesManager.props == null)
530: loadProps();
531: double returnValue = defaultValue;
532: try {
533: returnValue = getPropertyAsDouble(name);
534: } catch (Throwable t) {
535: log
536: .error(
537: "Could not retrieve or parse as double property ["
538: + name + "], defaulting to "
539: + defaultValue, t);
540: }
541: return returnValue;
542: }
543:
544: /**
545: * Get a Set of the names of properties that have been requested but were not set.
546: * @return a Set of the String names of missing properties.
547: * @since uPortal 2.4
548: */
549: public static Set getMissingProperties() {
550: return PropertiesManager.missingProperties;
551: }
552: }
|