org.socraticgrid.account.util.PropertyAccessor.java Source code

Java tutorial

Introduction

Here is the source code for org.socraticgrid.account.util.PropertyAccessor.java

Source

/*
 * ****************************************************************************************************************
 *  *
 *  * Copyright (C) 2012 by Cognitive Medical Systems, Inc (http://www.cognitivemedciine.com)
 *  *
 *  * Licensed 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
 *  *
 *  *     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.
 *  *
 *  ****************************************************************************************************************
 *
 * ****************************************************************************************************************
 *  * Socratic Grid contains components to which third party terms apply. To comply with these terms, the following
 *  * notice is provided:
 *  *
 *  * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 *  * Copyright (c) 2008, Nationwide Health Information Network (NHIN) Connect. All rights reserved.
 *  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 *  * the following conditions are met:
 *  *
 *  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *  *     following disclaimer.
 *  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *  *     following disclaimer in the documentation and/or other materials provided with the distribution.
 *  * - Neither the name of the NHIN Connect Project nor the names of its contributors may be used to endorse or
 *  *     promote products derived from this software without specific prior written permission.
 *  *
 *  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 *  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 *  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER
 *  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 *  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *  *
 *  * END OF TERMS AND CONDITIONS
 *  *
 *  ****************************************************************************************************************
 */

package org.socraticgrid.account.util;

import java.io.FileReader;
import java.io.File;

import java.text.SimpleDateFormat;
import java.util.Properties;
import java.util.Date;
import java.util.Hashtable;
import java.util.Calendar;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class is a POJO class that is used to access properties within a property
 * file. It is a clone of the
 *
 * @author Les Westberg, Jerry Goodnough
 * @version 1.1
 * @created 04-Sep-2008 1:21:31 PM
 */
public class PropertyAccessor {
    private static Log log = LogFactory.getLog(PropertyAccessor.class);
    private static final String KMR_PROPERTIES_DIR = "KMR_PROPERTIES_DIR";
    private static final String NHINC_PROPERTIES_DIR = "NHINC_PROPERTIES_DIR";
    private static final String CACHE_REFRESH_DURATION = "CacheRefreshDuration";
    private static final String CRLF = System.getProperty("line.separator");

    // This is a hash table that contains all the properties for all of the files that
    // have been accessed.  The hashtable is keyed off the property file name.
    //---------------------------------------------------------------------------------
    private static Hashtable<String, Properties> m_hAllProps = new Hashtable<String, Properties>();

    // This hash table is used to contain the information about when the next refresh is supposed
    // to take place on the property file.  The key is the name of the property file.  The
    // value is a RefreshInfo class that contains the refresh mode and the date/time that the
    // refresh should take place.
    //------------------------------------------------------------------------------------------------------
    private static Hashtable<String, RefreshInfo> m_hNextRefresh = new Hashtable<String, RefreshInfo>();
    private static String m_sPropertyFileDir = "";
    private static final String m_sFailedEnvVarMessage = "Unable to access environment variables: KMR_PROPERTIES_DIR or NHINC_PROPERTIES_DIR.";
    private static boolean m_bFailedToLoadEnvVar = false;

    static {
        // For macos the getenv call doesn't seem to find KMR_PROPERTIES_DIR so
        // set the KMR_PROPERTIES_DIR in the system properties in your glassfish
        // admin console and this call to getProperty here will find it.
        String sValue = "";

        if (System.getProperty("os.name").toLowerCase().indexOf("mac") > -1) {
            sValue = System.getProperty(KMR_PROPERTIES_DIR);
        } else {
            sValue = System.getenv(KMR_PROPERTIES_DIR);
        }

        if ((sValue != null) && (sValue.length() > 0)) {
            // Set it up so that we always have a "/" at the end - in case
            //------------------------------------------------------------
            if ((sValue.endsWith("/")) || (sValue.endsWith("\\"))) {
                m_sPropertyFileDir = sValue;
            } else {
                String sFileSeparator = System.getProperty("file.separator");
                m_sPropertyFileDir = sValue + sFileSeparator;
            }
        } else {
            sValue = System.getenv(NHINC_PROPERTIES_DIR);
            if ((sValue != null) && (sValue.length() > 0)) {
                // Set it up so that we always have a "/" at the end - in case
                //------------------------------------------------------------
                if ((sValue.endsWith("/")) || (sValue.endsWith("\\"))) {
                    m_sPropertyFileDir = sValue;
                } else {
                    String sFileSeparator = System.getProperty("file.separator");
                    m_sPropertyFileDir = sValue + sFileSeparator;
                }
            } else {
                sValue = "/home/nhin/Properties";
                log.error(m_sFailedEnvVarMessage);

                //  m_bFailedToLoadEnvVar = true;
            }
        }
    }

    /**
     * Default constructor.
     */
    public PropertyAccessor() {
    }

    public String getPropertiesDir() {
        return m_sPropertyFileDir;
    }

    /**
     * This method does a quick check to make sure that the environment variable has been
     * set.  It if has not, then an exception is thrown.
     *
     * @return  True if the environment variable is set.   (It never gives false, because
     *          if it is not set, it throws an exception).
     * @throws PropertyAccessException This is thrown if the environment variable was not set.
     */
    private static boolean checkEnvVarSet() throws PropertyAccessException {
        if (m_bFailedToLoadEnvVar) {
            throw new PropertyAccessException(m_sFailedEnvVarMessage);
        }

        return true; // We only get here if the env variable was loaded.
    }

    /**
     * This is a helper method that checks to see if the string is not
     * null and that it contains a value other than the empty string.  It throws
     * an exception if it is null or contains the empty string.
     *
     * @param sMethodName The name of the method that is calling this one.
     * @param sVarName The name of the variable that is being checked. (Used in the error message text.)
     * @param sValue The variable that is being checked.
     * @throws PropertyAccessException If the variable is null or empty this exception is thrown.
     */
    private static void stringIsValid(String sMethodName, String sVarName, String sValue)
            throws PropertyAccessException {
        if (sValue == null) {
            throw new PropertyAccessException("Method: " + sMethodName + ", Variable: " + sVarName
                    + " was null and it should have a valid value.");
        } else if ((sValue.length() == 0) || (sValue.trim().length() == 0)) {
            throw new PropertyAccessException("Method: " + sMethodName + ", Variable: " + sVarName
                    + " was '' (empty string) and it should have a valid value.");
        }
    }

    /**
     * This creates a new properties class with a full copy of all of the
     * properties.
     *
     * @param oProps The property file that is to be copied.
     * @return The copy that is returned.
     * @throws gov.hhs.fha.nhinc.properties.PropertyAccessException This exception is thrown if
     *         it cannot load the property file for some reason.
     */
    private static Properties deepCopyProperties(Properties oProps) throws PropertyAccessException {
        Properties oRetProps = new Properties();

        Set<String> setKeys = oProps.stringPropertyNames();
        Iterator<String> iterKeys = setKeys.iterator();
        while (iterKeys.hasNext()) {
            String sKey = iterKeys.next();
            String sValue = oProps.getProperty(sKey);
            if (sValue != null) {
                sValue = sValue.trim();
            }
            oRetProps.put(sKey, sValue);
        }

        return oRetProps;
    }

    /**
     * This method loads the property file and sets the refresh time.  If the property:
     * "CacheRefreshDuration" is found in the property file, then it will set it as follows:
     * If the value is "-1", then
     *
     * @param sPropertyFile The name of the property file to be loaded.
     * @param oInfo The refresh information that we already know - this may be null if this
     *              is the first time the file is being loaded.
     * @throws gov.hhs.fha.nhinc.properties.PropertyAccessException This exception is thrown if
     *         it cannot load the property file for some reason.
     */
    private static void loadPropertyFile(String sPropertyFile, RefreshInfo oInfo) throws PropertyAccessException {
        String sPropFilePathAndName = m_sPropertyFileDir + sPropertyFile + ".properties";
        RefreshInfo oNewInfo = null;
        if (oInfo != null) {
            oNewInfo = oInfo;

            // Reset the refresh time...
            //--------------------------
            if (oNewInfo.m_oRefreshMode == RefreshInfo.Mode.PERIODIC) {
                Calendar oRefreshDate = Calendar.getInstance();
                oRefreshDate.add(Calendar.MILLISECOND, oNewInfo.m_iRefreshMilliseconds);
                oNewInfo.m_dtRefreshDate = oRefreshDate.getTime();
            } else if (oNewInfo.m_oRefreshMode == RefreshInfo.Mode.ALWAYS) {
                oNewInfo.m_dtRefreshDate = Calendar.getInstance().getTime();
            }
        } else {
            // Default Values -
            //------------------
            oNewInfo = new RefreshInfo();
            oNewInfo.m_oRefreshMode = RefreshInfo.Mode.NEVER;
            oNewInfo.m_dtRefreshDate = new Date();
            oNewInfo.m_iRefreshMilliseconds = -1;
        }

        Properties oProps = new Properties();
        FileReader frPropFile = null;
        try {
            File fPropFile = new File(sPropFilePathAndName);
            if (!fPropFile.exists()) {
                throw new PropertyAccessException(
                        "Failed to open property file:'" + sPropFilePathAndName + "'.  " + "File does not exist.");
            }
            frPropFile = new FileReader(fPropFile);
            oProps.load(frPropFile);

            // Look to see if we have a refresh property setting.  If so, update
            // the settings we have had to the new ones...
            //-------------------------------------------------------------------
            String sValue = oProps.getProperty(CACHE_REFRESH_DURATION);
            if ((sValue != null) && (sValue.length() > 0)) {
                int iMilliseconds = -1;
                try {
                    iMilliseconds = Integer.parseInt(sValue.trim());
                } catch (Exception e1) {
                    log.warn("Property File:'" + sPropertyFile + "' contained an invalid '" + CACHE_REFRESH_DURATION
                            + "' value.  It is supposed to be numeric and it was not.  " + "The value was '"
                            + sValue + "'.  Treating this property file as 'refresh never'.");
                }

                // Update any refresh information that may now be in the file...
                //---------------------------------------------------------------
                if (iMilliseconds <= -1) {
                    oNewInfo.m_oRefreshMode = RefreshInfo.Mode.NEVER;
                    oNewInfo.m_iRefreshMilliseconds = -1;
                    oNewInfo.m_dtRefreshDate = null;
                } else if (iMilliseconds == 0) {
                    oNewInfo.m_oRefreshMode = RefreshInfo.Mode.ALWAYS;
                    oNewInfo.m_iRefreshMilliseconds = 0;
                    oNewInfo.m_dtRefreshDate = new Date();
                } else {
                    oNewInfo.m_oRefreshMode = RefreshInfo.Mode.PERIODIC;
                    oNewInfo.m_iRefreshMilliseconds = iMilliseconds;
                    Calendar oRefreshDate = Calendar.getInstance();
                    oRefreshDate.add(Calendar.MILLISECOND, iMilliseconds);
                    oNewInfo.m_dtRefreshDate = oRefreshDate.getTime();
                }
            } // if ((sValue != null) && (sValue.length() > 0))

            // Now lets refresh the property information
            //------------------------------------------
            synchronized (m_hNextRefresh) {
                m_hAllProps.put(sPropertyFile, oProps);
                m_hNextRefresh.put(sPropertyFile, oNewInfo);
            }

            //            if (log.isDebugEnabled())
            //            {
            //                String sMessage = "Loaded/Refreshed property file: " + sPropertyFile;
            //                log.debug(sMessage);
            //            }
        } catch (Exception e) {
            String sMessage = "Failed to load property file.  Error: " + e.getMessage();
            throw new PropertyAccessException(sMessage, e);
        } finally {
            if (frPropFile != null) {
                try {
                    frPropFile.close();
                } catch (Exception e1) {
                    log.error("Failed to close property file: '" + sPropFilePathAndName + "'", e1);
                }
            }
        }
    }

    /**
     * This method will check to see if the property file needs to be refreshed
     * and if it does, it will reload it.  Otherwise it will leave it as is.
     *
     * @param sPropertyFile The name of the property file that is being checked and
     *                      possibly loaded.
     * @throws PropertyAccessException If an error occurs during the load process,
     *                                 this exception is thrown.
     */
    private static void checkForRefreshAndLoad(String sPropertyFile) throws PropertyAccessException {
        Date dtNow = new Date();
        RefreshInfo oInfo = m_hNextRefresh.get(sPropertyFile);

        if (oInfo != null) {
            if ((oInfo.m_oRefreshMode == RefreshInfo.Mode.ALWAYS)
                    || ((oInfo.m_oRefreshMode == RefreshInfo.Mode.PERIODIC))
                            && (oInfo.m_dtRefreshDate.before(dtNow))) {
                loadPropertyFile(sPropertyFile, oInfo);
            }
        } else if (oInfo == null) {
            loadPropertyFile(sPropertyFile, oInfo); // This means that this is the firat time property file has been accessed
        }
    }

    /**
     * Check is the property file needs to be reloaded.
     *
     * @param sPropertyFile
     * @return
     * @throws PropertyAccessException
     */
    public static boolean isRefreshNeeded(String sPropertyFile) throws PropertyAccessException {
        Date dtNow = new Date();
        RefreshInfo oInfo = m_hNextRefresh.get(sPropertyFile);

        if (oInfo != null) {
            if ((oInfo.m_oRefreshMode == RefreshInfo.Mode.ALWAYS)
                    || ((oInfo.m_oRefreshMode == RefreshInfo.Mode.PERIODIC))
                            && (oInfo.m_dtRefreshDate.before(dtNow))) {
                return true;
            }
        }
        return false;
    }

    /**
     * This method returns the value of the given property that is located within the
     * given property file.   If the properties have been cached and the cache is
     * still fresh, then it will return the value from the cache.  If the properties
     * are cached, but the cache is not fresh, then the cache will be updated with the
     * current values in the properties file and then the property will be returned.
     * If the properties for that file are not cached at all, the property will be
     * retrieved from the properties file and returned.
     *
     * @param sPropertyFile    The name of the property file.  This is the name of the
     * file without a path and without the ".properties" extension.   Examples of this
     * would be "connection" or "gateway".
     * @param sPropertyName    This is the name of the property within the property
     * file.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static String getProperty(String sPropertyFile, String sPropertyName) throws PropertyAccessException {
        String sReturnValue = "";

        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("getProperty", "sPropertyFile", sPropertyFile);
        stringIsValid("getProperty", "sPropertyName", sPropertyName);

        checkForRefreshAndLoad(sPropertyFile);

        Properties oProps = m_hAllProps.get(sPropertyFile);

        if (oProps != null) {
            String sValue = oProps.getProperty(sPropertyName);
            if ((sValue != null) && (sValue.length() > 0)) {
                sReturnValue = sValue.trim();
            }
        }

        return sReturnValue;
    }

    /**
     * This will return true if the property value is: T, t, or any case combination
     * of "TRUE" and it will return false for all other values.
     *
     * @param sPropertyFile    The name of the property file.
     * @param sPropertyName    The name of the property that contains a boolean value.
     * This will return true if the value is: T, t, or any case combination of "TRUE"
     * and it will return false for all other values.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static boolean getPropertyBoolean(String sPropertyFile, String sPropertyName)
            throws PropertyAccessException {
        boolean bReturnValue = false;

        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("getPropertyBoolean", "sPropertyFile", sPropertyFile);
        stringIsValid("getPropertyBoolean", "sPropertyName", sPropertyName);

        checkForRefreshAndLoad(sPropertyFile);

        Properties oProps = m_hAllProps.get(sPropertyFile);

        if (oProps != null) {
            String sValue = oProps.getProperty(sPropertyName);
            if ((sValue != null) && (sValue.length() > 0)
                    && ((sValue.trim().equalsIgnoreCase("T")) || (sValue.trim().equalsIgnoreCase("TRUE")))) {
                bReturnValue = true;
            }
        }

        return bReturnValue;
    }

    /**
     * This will return the long value conversion of the property.  If the property value
     * cannot be converted to a long, an exception will be thrown.
     *
     * @param sPropertyFile    The name of the property file.
     * @param sPropertyName    The name of the property that contains a boolean value.
     * @return This will return the long representation of the value.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static long getPropertyLong(String sPropertyFile, String sPropertyName) throws PropertyAccessException {
        long lReturnValue = 0;

        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("getPropertyBoolean", "sPropertyFile", sPropertyFile);
        stringIsValid("getPropertyBoolean", "sPropertyName", sPropertyName);

        checkForRefreshAndLoad(sPropertyFile);

        Properties oProps = m_hAllProps.get(sPropertyFile);

        if (oProps != null) {
            String sValue = oProps.getProperty(sPropertyName);
            if ((sValue != null) && (sValue.length() > 0)) {
                try {
                    lReturnValue = Long.parseLong(sValue.trim());
                } catch (Exception e) {
                    String sError = "Failed to convert string value: '" + sValue + "' to a long.  Error: "
                            + e.getMessage();
                    log.warn(sError, e);
                    throw new PropertyAccessException(sError, e);
                }
            }
        }

        return lReturnValue;
    }

    /**
     * This method returns the set of keys in a property file.
     *
     * @param sPropertyFile The name of the property file.
     * @return An enumeration of property keys in the property file.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static final Set<String> getPropertyNames(String sPropertyFile) throws PropertyAccessException {
        Set<String> setPropNames = null;

        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("getPropertyNames", "sPropertyFile", sPropertyFile);

        checkForRefreshAndLoad(sPropertyFile);

        Properties oProps = m_hAllProps.get(sPropertyFile);

        if (oProps != null) {
            setPropNames = oProps.stringPropertyNames();
        }

        return setPropNames;
    }

    /**
     * This method returns the properties that are located within the given property
     * file.   If the properties have been cached and the cache is still fresh, then
     * it will return the values from the cache.  If the properties are cached, but
     * the cache is not fresh, then the cache will be updated with the current values
     * in the properties file and then the property values will be returned.  If the
     * properties for that file are not cached at all, the property will be retrieved
     * from the properties file and returned.
     *
     * NOTE:  THIS IS AN EXPENSIVE OPERATION.  IT WILL CREATE A DEEP COPY OF THE
     *        PROPERTIES AND RETURN IT.  THAT MEANS IT WILL CREATE AN EXACT REPLICA
     *        WITH ALL DATA.  THIS IS A PROTECTION TO MAKE SURE THAT A PROPERTY
     *        IS NOT INADVERTANTLY CHANGED OUTSIDE OF THIS CLASS.
     *
     * @param sPropertyFile    The name of the properties file without the path or
     *                         extension.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static final Properties getProperties(String sPropertyFile) throws PropertyAccessException {
        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("getProperties", "sPropertyFile", sPropertyFile);

        checkForRefreshAndLoad(sPropertyFile);

        Properties oProps = m_hAllProps.get(sPropertyFile);

        Properties oRetProps = deepCopyProperties(oProps);

        return oRetProps;
    }

    /**
     * This will return the in milliseconds the refresh duration on the property file.
     * A setting of -1 means it never refreshes.
     *
     * @param sPropertyFile    The name of the property file.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static int getRefreshDuration(String sPropertyFile) throws PropertyAccessException {
        int iRefreshDuration = -1;

        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("getRefreshDuration", "sPropertyFile", sPropertyFile);

        checkForRefreshAndLoad(sPropertyFile);

        RefreshInfo oInfo = m_hNextRefresh.get(sPropertyFile);
        if (oInfo != null) {
            iRefreshDuration = oInfo.m_iRefreshMilliseconds;
        }

        return iRefreshDuration;
    }

    /**
     * This will return the duration in milliseconds before the next refresh of the
     * properties file.  A value of -1 indicates that no refresh will occur.
     *
     * @param sPropertyFile    The name of the property file.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static int getDurationBeforeNextRefresh(String sPropertyFile) throws PropertyAccessException {
        int iNextRefreshDuration = -1;

        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("getDurationBeforeNextRefresh", "sPropertyFile", sPropertyFile);

        checkForRefreshAndLoad(sPropertyFile);

        RefreshInfo oInfo = m_hNextRefresh.get(sPropertyFile);
        if (oInfo != null) {
            if (oInfo.m_oRefreshMode == RefreshInfo.Mode.ALWAYS) {
                iNextRefreshDuration = 0;
            } else if (oInfo.m_oRefreshMode == RefreshInfo.Mode.NEVER) {
                iNextRefreshDuration = -1;
            } else {
                long lNowMilli = new Date().getTime();
                long lRefreshMilli = oInfo.m_dtRefreshDate.getTime();
                iNextRefreshDuration = (int) (lRefreshMilli - lNowMilli);
            }
        }

        return iNextRefreshDuration;
    }

    /**
     * If a property file has been cached, this will force a refresh of the property
     * file.  If a property file is not cached, then this operation will do nothing.
     *
     * @param sPropertyFile The name of the property file.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static void forceRefresh(String sPropertyFile) throws PropertyAccessException {
        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("forceRefresh", "sPropertyFile", sPropertyFile);

        RefreshInfo oInfo = m_hNextRefresh.get(sPropertyFile);
        // This means we have never loaded the file - so load it now...
        //--------------------------------------------------------------
        if (oInfo == null) {
            checkForRefreshAndLoad(sPropertyFile);
        } else if (oInfo.m_oRefreshMode != RefreshInfo.Mode.NEVER) {
            loadPropertyFile(sPropertyFile, oInfo);
        }
    }

    /**
     * This method will return the location of the property files.  Essentially it
     * will return the value in the NHINC_PROPERTIES_DIR environment variable.
     */
    public static String getPropertyFileLocation() {
        return m_sPropertyFileDir;
    }

    /**
     * This method dumps the properties and associated values for a properties file to
     * the log file.
     *
     * @param sPropertyFile    The name of the property file.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    public static void dumpPropsToLog(String sPropertyFile) throws PropertyAccessException {
        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("dumpPropsToLog", "sPropertyFile", sPropertyFile);

        StringBuffer sbLogMessage = new StringBuffer();

        sbLogMessage.append("Dumping contents of property file: '" + sPropertyFile + "'." + CRLF);

        RefreshInfo oInfo = m_hNextRefresh.get(sPropertyFile);
        // This means we have never loaded the file - so load it now...
        //--------------------------------------------------------------
        if (oInfo != null) {
            sbLogMessage.append("RefreshInfo.RefreshMode=" + oInfo.m_oRefreshMode + CRLF);
            sbLogMessage.append("RefreshInfo.RefreshMilliseconds=" + oInfo.m_iRefreshMilliseconds + CRLF);
            if (oInfo.m_dtRefreshDate != null) {
                SimpleDateFormat oFormat = new SimpleDateFormat("MM/dd/yyyy.HH:mm:ss");
                sbLogMessage.append("RefreshInfo.RefreshDate=" + oFormat.format(oInfo.m_dtRefreshDate) + CRLF);
            } else {
                sbLogMessage.append("RefreshInfo.RefreshDate=null" + CRLF);
            }

            sbLogMessage.append("Properties:");

            Properties oProps = m_hAllProps.get(sPropertyFile);
            if (oProps != null) {
                Set<String> setKeys = oProps.stringPropertyNames();
                if (setKeys != null) {
                    Iterator<String> iterKeys = setKeys.iterator();
                    while (iterKeys.hasNext()) {
                        String sKey = iterKeys.next();
                        String sValue = oProps.getProperty(sKey);
                        if (sValue != null) {
                            sValue = sValue.trim();
                        }
                        sbLogMessage.append("Property:" + sKey + "=" + sValue + CRLF);
                    }
                } // if (setKeys != null)
                else {
                    sbLogMessage.append("No properties were found in the property file." + CRLF);
                }
            } // if (oProps != null)
            else {
                sbLogMessage.append("No properties were found in the property file." + CRLF);
            }
        } //if (oInfo != null)
        else {
            sbLogMessage.append("No content.  Property file has never been loaded." + CRLF);
        }

        log.info(sbLogMessage.toString());
    }

    /**
     * This method is protected.  It is only to be used for unit testing and should
     * NEVER be used in the run-time environment. It's purpose is to allow properties
     * to be staged for a unit test.  It is marked as protected so that only classes
     * that are derived from this one can use it.  Note that this sets the property in
     * the cache memory only.   Because of the nature of this setting...   If a
     * property file is marked as not cached, or is marked with a cache refresh, the
     * setting of a property will override that and it will now be marked as cached
     * only and never refresh.  The purpose for this is two fold:   1) You do not want
     * the property file to be unknowningly changed.  2) Since a controlled
     * environment is required in testing, it is not wise to have the properties reset
     * back to the values in the stored file during the middle of a test.
     *
     * @param sPropertyFile    The name of the property file.
     * @param sPropertyName    The name of the property.
     * @param sValue    The value to set for the property.  NOTE:  This will only set
     *                  the property in memory (in the cache).  It will not alter the file itself.
     * @throws PropertyAccessException This is thrown if an error occurs accessing the property.
     */
    protected static void setProperty(String sPropertyFile, String sPropertyName, String sValue)
            throws PropertyAccessException {
        // Make sure everything is in a good state.
        //-----------------------------------------
        checkEnvVarSet();
        stringIsValid("setProperty", "sPropertyFile", sPropertyFile);
        stringIsValid("setProperty", "sPropertyName", sPropertyName);

        // Check for null and set it to empty string...
        //---------------------------------------------
        String sPropValue = sValue;
        if (sPropValue == null) {
            sPropValue = "";
        }

        // This call is primarily for the case where the file has not
        // yet been loaded.  We need to make sure it gets laoded.  After that
        // if a setProperty is called the mode will be set to Never, and this
        // call will essentially turn into a NOOP.
        //---------------------------------------------------------------------
        checkForRefreshAndLoad(sPropertyFile);

        RefreshInfo oInfo = m_hNextRefresh.get(sPropertyFile);
        if (oInfo == null) {
            String sMessage = "Property file: '" + sPropertyFile
                    + "' does not exist.  You cannot set a property for a file that does not exist.";
            log.error(sMessage);
            throw new PropertyAccessException(sMessage);
        }

        // Make sure we set this to "Never" refresh...
        //---------------------------------------------
        if (oInfo.m_oRefreshMode != RefreshInfo.Mode.NEVER) {
            synchronized (m_hNextRefresh) {
                oInfo.m_oRefreshMode = RefreshInfo.Mode.NEVER;
                oInfo.m_iRefreshMilliseconds = -1;
                oInfo.m_dtRefreshDate = null;
                m_hNextRefresh.put(sPropertyFile, oInfo);
            }

            // Put out a warning that a property file has been modified with a run-time modification.
            //---------------------------------------------------------------------------------------
            log.warn("Property File: " + sPropertyFile + " is being changed to 'NEVER' refresh because it is being "
                    + "modified at run-time.");
        }

        // Now update the property.
        //-------------------------
        Properties oProps = m_hAllProps.get(sPropertyFile);
        if (oProps == null) {
            String sMessage = "Property file: '" + sPropertyFile
                    + "' does not exist.  You cannot set a property for a file that does not exist.";
            log.error(sMessage);
            throw new PropertyAccessException(sMessage);
        }
        synchronized (m_hNextRefresh) {
            oProps.setProperty(sPropertyName, sValue);
        }
        // Put out a warning that a property file has been modified with a run-time modification.
        //---------------------------------------------------------------------------------------
        log.warn("Property File: " + sPropertyFile + ", PropertyName=" + sPropertyName
                + " is being changed at run-time to:'" + sValue + "'.");
    }

    /**
     * This class is used to hold refresh information for a properties file.
     */
    private static class RefreshInfo {
        public static enum Mode {
            NEVER, ALWAYS, PERIODIC
        };

        Mode m_oRefreshMode;
        Date m_dtRefreshDate;
        int m_iRefreshMilliseconds;
    }
}