Java tutorial
/** * Licensed to Apereo under one or more contributor license * agreements. See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * Apereo licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a * copy of the License at the following location: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apereo.portal.properties; import java.util.Collections; import java.util.HashSet; import java.util.Properties; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Provides access to properties. * <p>It is important to understand that usage of this class is different from what you might * be used to in java.util.Properties. Specifically, when you get a Properties property, * if that property is not set, the return value is NULL. However, when you call the basic getters here, * if the property is not set, a RuntimeException is thrown. These methods will never return null (except * if you pass in null as the default return value for the methods that take a default).</p> * <p>There are methods to get properties as various primitive types, int, double, float, etc. * When you invoke one of these methods on a property that is found but cannot be parsed as * your desired type, a RuntimeException is thrown.</p> * <p>There are * corresponding methods which take as a second parameter a default value. These methods, instead of * throwing a RuntimeException when the property cannot be found, return the default value. You can * use the default value "null" to invoke getProperty() with semantics more like the java.util.Properties object. * These augmented accessors which take defaults will be, I hope, especially useful in static initializers. Providing a * default in your static initializer will keep your class from blowing up at initialization when your property cannot be found. * This seems especially advantageous when there is a plausible default value.</p> * <p>This class has a comprehensive JUnit testcase. Please keep the testcase up to date with any changes you make to this class.</p> * @author Ken Weiner, kweiner@unicon.net * @author howard.gilbert@yale.edu * @author andrew.petro@yale.edu * @since uPortal 2.4, this class existed in the main package since uPortal 2.0 */ public class PropertiesManager { protected static final Log log = LogFactory.getLog(PropertiesManager.class); public static final String PORTAL_PROPERTIES_FILE_SYSTEM_VARIABLE = "portal.properties"; private static final String PORTAL_PROPERTIES_FILE_NAME = "/properties/portal.properties"; private static Properties props = null; /** * A set of the names of properties that clients of this class attempt to access * but which were not set in the properties file. * This Set allows this class to report about missing properties and to * log each missing property only the first time it is requested. */ private static final Set missingProperties = Collections.synchronizedSet(new HashSet()); /** * Setter method to set the underlying Properties. * This is a public method to allow poor-man's static dependency injection of the Properties from wherever you want to get them. * If Properties have not been injected before any accessor method is invoked, PropertiesManager will invoke loadProperties() to attempt * to load its own properties. You might call this from a context listener, say. * If Properties have already been loaded or injected, this method will overwrite them. * @param props - Properties to be injected. */ public static synchronized void setProperties(Properties props) { PropertiesManager.props = props; } /** * Load up the portal properties. Right now the portal properties is a simple * .properties file with name value pairs. It may evolve to become an XML file * later on. */ protected static void loadProps() { PropertiesManager.props = new Properties(); try { String pfile = System.getProperty(PORTAL_PROPERTIES_FILE_SYSTEM_VARIABLE); if (pfile == null) { pfile = PORTAL_PROPERTIES_FILE_NAME; } PropertiesManager.props.load(PropertiesManager.class.getResourceAsStream(pfile)); } catch (Throwable t) { log.error("Unable to read portal.properties file.", t); } } /** * Returns the value of a property for a given name. * Any whitespace is trimmed off the beginning and * end of the property value. * Note that this method will never return null. * If the requested property cannot be found, this method throws an UndeclaredPortalException. * @param name the name of the requested property * @return value the value of the property matching the requested name * @throws MissingPropertyException - if the requested property cannot be found */ public static String getProperty(String name) throws MissingPropertyException { if (log.isTraceEnabled()) { log.trace("entering getProperty(" + name + ")"); } if (PropertiesManager.props == null) loadProps(); String val = getPropertyUntrimmed(name); val = val.trim(); if (log.isTraceEnabled()) { log.trace("returning from getProperty(" + name + ") with return value [" + val + "]"); } return val; } /** * Returns the value of a property for a given name * including whitespace trailing the property value, but not including * whitespace leading the property value. * An UndeclaredPortalException is thrown if the property cannot be found. * This method will never return null. * @param name the name of the requested property * @return value the value of the property matching the requested name * @throws MissingPropertyException - (undeclared) if the requested property is not found */ public static String getPropertyUntrimmed(String name) throws MissingPropertyException { if (PropertiesManager.props == null) loadProps(); if (props == null) { boolean alreadyReported = registerMissingProperty(name); throw new MissingPropertyException(name, alreadyReported); } String val = props.getProperty(name); if (val == null) { boolean alreadyReported = registerMissingProperty(name); throw new MissingPropertyException(name, alreadyReported); } return val; } /** * Returns the value of a property for a given name. * This method can be used if the property is boolean in * nature and you want to make sure that <code>true</code> is * returned if the property is set to "true", "yes", "y", or "on" * (regardless of case), * and <code>false</code> is returned in all other cases. * @param name the name of the requested property * @return value <code>true</code> if property is set to "true", "yes", "y", or "on" regardless of case, otherwise <code>false</code> * @throws MissingPropertyException - when no property of the given name is declared. */ public static boolean getPropertyAsBoolean(String name) throws MissingPropertyException { if (PropertiesManager.props == null) loadProps(); boolean retValue = false; String value = getProperty(name); if (value != null) { if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("y") || value.equalsIgnoreCase("on")) { retValue = true; } else if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no") || value.equalsIgnoreCase("n") || value.equalsIgnoreCase("off")) { retValue = false; } else { // this method's historical behavior, maintained here, is to return false // for all values that did not match on of the true values above. log.error("property [" + name + "] is being accessed as a boolean " + "but had non-canonical value [" + value + "]. Returning it as false, " + "but this may be a property misconfiguration."); } } else { log.fatal("property [" + name + "] is being accessed as a boolean " + "but was null. Returning false. However, it should not have been " + "possible to get here because getProperty() throws a runtime " + "exception or returns a non-null value."); } return retValue; } /** * Returns the value of a property for a given name as a <code>byte</code> * @param name the name of the requested property * @return value the property's value as a <code>byte</code> * @throws MissingPropertyException - if the property is not set * @throws BadPropertyException - if the property cannot be parsed as a byte */ public static byte getPropertyAsByte(String name) throws MissingPropertyException, BadPropertyException { if (PropertiesManager.props == null) loadProps(); try { return Byte.parseByte(getProperty(name)); } catch (NumberFormatException nfe) { throw new BadPropertyException(name, getProperty(name), "byte"); } } /** * Returns the value of a property for a given name as a <code>short</code> * @param name the name of the requested property * @return value the property's value as a <code>short</code> * @throws MissingPropertyException - if the property is not set * @throws BadPropertyException - if the property cannot be parsed as a short or is not set. */ public static short getPropertyAsShort(String name) throws MissingPropertyException, BadPropertyException { if (PropertiesManager.props == null) loadProps(); try { return Short.parseShort(getProperty(name)); } catch (NumberFormatException nfe) { throw new BadPropertyException(name, getProperty(name), "short"); } } /** * Returns the value of a property for a given name as an <code>int</code> * @param name the name of the requested property * @return value the property's value as an <code>int</code> * @throws MissingPropertyException - if the property is not set * @throws BadPropertyException - if the property cannot be parsed as an int */ public static int getPropertyAsInt(String name) throws MissingPropertyException, BadPropertyException { if (PropertiesManager.props == null) loadProps(); try { return Integer.parseInt(getProperty(name)); } catch (NumberFormatException nfe) { throw new BadPropertyException(name, getProperty(name), "int"); } } /** * Returns the value of a property for a given name as a <code>long</code> * @param name the name of the requested property * @return value the property's value as a <code>long</code> * @throws MissingPropertyException - if the property is not set * @throws BadPropertyException - if the property cannot be parsed as a long */ public static long getPropertyAsLong(String name) throws MissingPropertyException, BadPropertyException { if (PropertiesManager.props == null) loadProps(); try { return Long.parseLong(getProperty(name)); } catch (NumberFormatException nfe) { throw new BadPropertyException(name, getProperty(name), "long"); } } /** * Returns the value of a property for a given name as a <code>float</code> * @param name the name of the requested property * @return value the property's value as a <code>float</code> * @throws MissingPropertyException - if the property is not set * @throws BadPropertyException - if the property cannot be parsed as a float */ public static float getPropertyAsFloat(String name) throws MissingPropertyException, BadPropertyException { if (PropertiesManager.props == null) loadProps(); try { return Float.parseFloat(getProperty(name)); } catch (NumberFormatException nfe) { throw new BadPropertyException(name, getProperty(name), "float"); } } /** * Returns the value of a property for a given name as a <code>long</code> * @param name the name of the requested property * @return value the property's value as a <code>double</code> * @throws MissingPropertyException - if the property has not been set * @throws BadPropertyException - if the property cannot be parsed as a double or is not set. */ public static double getPropertyAsDouble(String name) throws MissingPropertyException, BadPropertyException { if (PropertiesManager.props == null) loadProps(); try { return Double.parseDouble(getProperty(name)); } catch (NumberFormatException nfe) { throw new BadPropertyException(name, getProperty(name), "double"); } } /** * Registers that a given property was sought but not found. * Currently adds the property to the set of missing properties and * logs if this is the first time the property has been requested. * @param name - the name of the missing property * @return true if the property was previously registered, false otherwise * */ private static boolean registerMissingProperty(String name) { final boolean previouslyReported = !PropertiesManager.missingProperties.add(name); if (!previouslyReported && log.isInfoEnabled()) { log.info("Property [" + name + "] was requested but not found."); } return previouslyReported; } /** * Get the value of the property with the given name. * If the named property is not found, returns the supplied default value. * This error handling behavior makes this method attractive for use in static initializers. * @param name - the name of the property to be retrieved. * @param defaultValue - a fallback default value which will be returned if the property cannot be found. * @return the value of the requested property, or the supplied default value if the named property cannot be found. * @since uPortal 2.4 */ public static String getProperty(String name, String defaultValue) { if (PropertiesManager.props == null) loadProps(); String returnValue = defaultValue; try { returnValue = getProperty(name); } catch (MissingPropertyException mpe) { // Do nothing, since we have already recorded and logged the missing property. } return returnValue; } /** * Get a property as a boolean, specifying a default value. * If for any reason we are unable to lookup the desired property, * this method returns the supplied default value. * This error handling behavior makes this method suitable for calling from static initializers. * @param name - the name of the property to be accessed * @param defaultValue - default value that will be returned in the event of any error * @return the looked up property value, or the defaultValue if any problem. * @since uPortal 2.4 */ public static boolean getPropertyAsBoolean(final String name, final boolean defaultValue) { if (PropertiesManager.props == null) loadProps(); boolean returnValue = defaultValue; try { returnValue = getPropertyAsBoolean(name); } catch (MissingPropertyException mpe) { // do nothing, since we already logged the missing property } return returnValue; } /** * Get the value of the given property as a byte, specifying a fallback default value. * If for any reason we are unable to lookup the desired property, * this method returns the supplied default value. * This error handling behavior makes this method suitable for calling from static initializers. * @param name - the name of the property to be accessed * @param defaultValue - the default value that will be returned in the event of any error * @return the looked up property value, or the defaultValue if any problem. * @since uPortal 2.4 */ public static byte getPropertyAsByte(final String name, final byte defaultValue) { if (PropertiesManager.props == null) loadProps(); byte returnValue = defaultValue; try { returnValue = getPropertyAsByte(name); } catch (Throwable t) { log.error("Could not retrieve or parse as byte property [" + name + "], defaulting to [" + defaultValue + "]", t); } return returnValue; } /** * Returns the value of a property for a given name as a short. * If for any reason the property cannot be looked up as a short, returns the supplied default value. * This error handling makes this method a good choice for static initializer calls. * @param name - the name of the requested property * @param defaultValue - a default value that will be returned in the event of any error * @return the property value as a short or the default value in the event of any error * @since uPortal 2.4 */ public static short getPropertyAsShort(String name, short defaultValue) { if (PropertiesManager.props == null) loadProps(); short returnValue = defaultValue; try { returnValue = getPropertyAsShort(name); } catch (Throwable t) { log.error("Could not retrieve or parse as short property [" + name + "], defaulting to given value [" + defaultValue + "]", t); } return returnValue; } /** * Get the value of a given property as an int. * If for any reason the property cannot be looked up as an int, returns the supplied default value. * This error handling makes this method a good choice for static initializer calls. * @param name - the name of the requested property * @param defaultValue - a fallback default value for the property * @return the value of the property as an int, or the supplied default value in the event of any problem. * @since uPortal 2.4 */ public static int getPropertyAsInt(String name, int defaultValue) { if (PropertiesManager.props == null) loadProps(); int returnValue = defaultValue; try { returnValue = getPropertyAsInt(name); } catch (Throwable t) { log.error( "Could not retrieve or parse as int the property [" + name + "], defaulting to " + defaultValue, t); } return returnValue; } /** * Get the value of the given property as a long. * If for any reason the property cannot be looked up as a long, returns the supplied default value. * This error handling makes this method a good choice for static initializer calls. * @param name - the name of the requested property * @param defaultValue - a fallback default value that will be returned if there is any problem * @return the value of the property as a long, or the supplied default value if there is any problem. * @since uPortal 2.4 */ public static long getPropertyAsLong(String name, long defaultValue) { if (PropertiesManager.props == null) loadProps(); long returnValue = defaultValue; try { returnValue = getPropertyAsLong(name); } catch (Throwable t) { log.error("Could not retrieve or parse as long property [" + name + "], defaulting to " + defaultValue, t); } return returnValue; } /** * Get the value of the given property as a float. * If for any reason the property cannot be looked up as a float, returns the supplied default value. * This error handling makes this method a good choice for static initializer calls. * @param name - the name of the requested property * @param defaultValue - a fallback default value that will be returned if there is any problem * @return the value of the property as a float, or the supplied default value if there is any problem. * @since uPortal 2.4 */ public static float getPropertyAsFloat(String name, float defaultValue) { if (PropertiesManager.props == null) loadProps(); float returnValue = defaultValue; try { returnValue = getPropertyAsFloat(name); } catch (Throwable t) { log.error("Could not retrieve or parse as float property [" + name + "], defaulting to " + defaultValue, t); } return returnValue; } /** * Get the value of the given property as a double. * If for any reason the property cannot be looked up as a double, returns the specified default value. * This error handling makes this method a good choice for static initializer calls. * @param name - the name of the requested property * @param defaultValue - a fallback default value that will be returned if there is any problem * @return the value of the property as a double, or the supplied default value if there is any problem. * @since uPortal 2.4 */ public static double getPropertyAsDouble(String name, double defaultValue) { if (PropertiesManager.props == null) loadProps(); double returnValue = defaultValue; try { returnValue = getPropertyAsDouble(name); } catch (Throwable t) { log.error( "Could not retrieve or parse as double property [" + name + "], defaulting to " + defaultValue, t); } return returnValue; } /** * Get a Set of the names of properties that have been requested but were not set. * @return a Set of the String names of missing properties. * @since uPortal 2.4 */ public static Set getMissingProperties() { return PropertiesManager.missingProperties; } }