/** * '$RCSfile$' * Purpose: A Class that implements properties methods for metacat * Copyright: 2008 Regents of the University of California and the * National Center for Ecological Analysis and Synthesis * Authors: Michael Daigle * * '$Author: daigle $' * '$Date: 2009-08-14 17:38:05 -0700 (Fri, 14 Aug 2009) $' * '$Revision: 5028 $' * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.ucsb.nceas.metacat.properties; import java.io.IOException; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.xml.transform.TransformerException; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.dataone.configuration.Settings; import edu.ucsb.nceas.metacat.service.ServiceService; import edu.ucsb.nceas.metacat.shared.BaseService; import edu.ucsb.nceas.metacat.shared.MetacatUtilException; import edu.ucsb.nceas.metacat.shared.ServiceException; import edu.ucsb.nceas.metacat.util.SystemUtil; import edu.ucsb.nceas.utilities.FileUtil; import edu.ucsb.nceas.utilities.GeneralPropertyException; import edu.ucsb.nceas.utilities.MetaDataProperty; import edu.ucsb.nceas.utilities.PropertiesMetaData; import edu.ucsb.nceas.utilities.PropertyNotFoundException; import edu.ucsb.nceas.utilities.SortedProperties; /** * A suite of utility classes for the metadata configuration utility */ public class ConfigurableProperties extends BaseService implements PropertiesInterface { private static final String MAIN_CONFIG_FILE_NAME = "metacat.properties"; private static String mainConfigFilePath = null; private static SortedProperties mainProperties = null; private static final String MAIN_METADATA_FILE_NAME = "metacat.properties.metadata.xml"; private static String mainMetadataFilePath = null; private static PropertiesMetaData mainMetaData = null; private static final String MAIN_BACKUP_FILE_NAME = "metacat.properties.backup"; private static String mainBackupFilePath = null; private static SortedProperties mainBackupProperties = null; private static final String AUTH_METADATA_FILE_NAME = "auth.properties.metadata.xml"; private static String authMetadataFilePath = null; private static PropertiesMetaData authMetaData = null; private static final String AUTH_BACKUP_FILE_NAME = "auth.properties.backup"; private static String authBackupFilePath = null; private static SortedProperties authBackupProperties = null; private static boolean bypassAlreadyChecked = false; private static Logger logMetacat = Logger.getLogger(ConfigurableProperties.class); /** * private constructor since this is a singleton * * @param servletContext the context we will use to get relative paths */ protected ConfigurableProperties() throws ServiceException { _serviceName = "ConfigurableProperties"; initialize(); } public boolean refreshable() { return true; } public void doRefresh() throws ServiceException { initialize(); } public void stop() throws ServiceException { return; } /** * Initialize the singleton. * * @param servletContext the context we will use to get relative paths */ private void initialize() throws ServiceException { logMetacat.debug("Initializing ConfigurableProperties"); try { mainConfigFilePath = PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_CONFIG_FILE_NAME; mainMetadataFilePath = PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_METADATA_FILE_NAME; // mainBackupFilePath = // PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_BACKUP_FILE_NAME; authMetadataFilePath = PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + AUTH_METADATA_FILE_NAME; // authBackupFilePath = // PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + AUTH_BACKUP_FILE_NAME; // mainProperties will hold the primary configuration values for // metacat. mainProperties = new SortedProperties(mainConfigFilePath); mainProperties.load(); // include main metacat properties in d1 properties as overrides try { Settings.getConfiguration(); Settings.augmentConfiguration(mainConfigFilePath); } catch (ConfigurationException e) { logMetacat.error("Could not augment DataONE properties. " + e.getMessage(), e); } // mainMetaData holds configuration information about main // properties. This is primarily used to display input fields on // the configuration page. The information is retrieved from an // xml metadata file mainMetaData = new PropertiesMetaData(mainMetadataFilePath); // authMetaData holds configuration information about organization // level // properties. This is primarily used to display input fields on // the auth configuration page. The information is retrieved // from an xml metadata file dedicated just to auth properties. authMetaData = new PropertiesMetaData(authMetadataFilePath); String recommendedExternalDir = SystemUtil.discoverExternalDir(); PropertyService.setRecommendedExternalDir(recommendedExternalDir); String backupPath = getProperty("application.backupDir"); if (backupPath == null || backupPath.equals("")) { backupPath = SystemUtil.getStoredBackupDir(); } if ((backupPath == null || backupPath.equals("")) && recommendedExternalDir != null) { backupPath = recommendedExternalDir + FileUtil.getFS() + "." + ServiceService.getRealApplicationContext(); } // if backupPath is still null, no reason to initialize the // backup properties. The system will need to prompt the user for // the backup properties and reinitialize ConfigurableProperties. if (backupPath != null && !backupPath.equals("")) { setProperty("application.backupDir", backupPath); SystemUtil.writeStoredBackupFile(backupPath); // The mainBackupProperties hold properties that were backed up // the last time the application was configured. On disk, the // file will look like a smaller version of metacat.properties. // It is stored in the data storage directory outside the // application directories. mainBackupFilePath = backupPath + FileUtil.getFS() + MAIN_BACKUP_FILE_NAME; mainBackupProperties = new SortedProperties(mainBackupFilePath); mainBackupProperties.load(); // The authBackupProperties hold properties that were backed up // the last time the auth was configured. On disk, the file // will look like a smaller version of metacat.properties. It // is stored in the data storage directory outside the // application directories. authBackupFilePath = backupPath + FileUtil.getFS() + AUTH_BACKUP_FILE_NAME; authBackupProperties = new SortedProperties(authBackupFilePath); authBackupProperties.load(); } } catch (TransformerException te) { throw new ServiceException("Transform problem while loading properties: " + te.getMessage()); } catch (IOException ioe) { throw new ServiceException("I/O problem while loading properties: " + ioe.getMessage()); } catch (GeneralPropertyException gpe) { throw new ServiceException("General properties problem while loading properties: " + gpe.getMessage()); } catch (MetacatUtilException ue) { throw new ServiceException("Utilities problem while loading properties: " + ue.getMessage()); } } /** * Utility method to get a property value from the properties file * * @param propertyName * the name of the property requested * @return the String value for the property */ public String getProperty(String propertyName) throws PropertyNotFoundException { return mainProperties.getProperty(propertyName); } /** * Get a set of all property names. * * @return Set of property names */ public Vector getPropertyNames() { return mainProperties.getPropertyNames(); } /** * Get a Set of all property names that start with the groupName prefix. * * @param groupName * the prefix of the keys to search for. * @return enumeration of property names */ public Vector getPropertyNamesByGroup(String groupName) { return mainProperties.getPropertyNamesByGroup(groupName); } /** * Get a Map of all properties that start with the groupName prefix. * * @param groupName * the prefix of the keys to search for. * @return Map of property names */ public Map getPropertiesByGroup(String groupName) throws PropertyNotFoundException { return mainProperties.getPropertiesByGroup(groupName); } /** * Utility method to add a property value both in memory and to the * properties file * * @param propertyName * the name of the property to add * @param newValue * the value for the property */ public void addProperty(String propertyName, String value) throws GeneralPropertyException { mainProperties.addProperty(propertyName, value); mainProperties.store(); } /** * Utility method to set a property value both in memory and to the * properties file * * @param propertyName * the name of the property requested * @param newValue * the new value for the property */ public void setProperty(String propertyName, String newValue) throws GeneralPropertyException { mainProperties.setProperty(propertyName, newValue); mainProperties.store(); } /** * Utility method to set a property value in memory. This will NOT cause the * property to be written to disk. Use this method to set multiple * properties in a row without causing excessive I/O. You must call * persistProperties() once you're done setting properties to have them * written to disk. * * @param propertyName * the name of the property requested * @param newValue * the new value for the property */ public void setPropertyNoPersist(String propertyName, String newValue) throws GeneralPropertyException { mainProperties.setPropertyNoPersist(propertyName, newValue); } /** * Save the properties to a properties file. Note, the * order and comments will be preserved. */ public void persistProperties() throws GeneralPropertyException { mainProperties.store(); } /** * Get the main backup properties file. These are configurable properties that * are stored outside the metacat install directories so the user does not * need to re-enter all the configuration information every time they do an * upgrade. * * @return a SortedProperties object with the backup properties */ public SortedProperties getMainBackupProperties() { return mainBackupProperties; } /** * Get the auth backup properties file. These are configurable * properties that are stored outside the metacat install directories so * the user does not need to re-enter all the configuration information * every time they do an upgrade. * * @return a SortedProperties object with the backup properties */ public SortedProperties getAuthBackupProperties() { return authBackupProperties; } /** * Get the main properties metadata. This is retrieved from an xml file that * describes the attributes of configurable properties. * * @return a PropertiesMetaData object with the main properties metadata */ public PropertiesMetaData getMainMetaData() { return mainMetaData; } /** * Get the auth properties metadata. This is retrieved from an xml * file that describes the attributes of configurable properties. * * @return a PropertiesMetaData object with the organization properties * metadata */ public PropertiesMetaData getAuthMetaData() { return authMetaData; } /** * Writes out backup configurable properties to a file. */ public void persistMainBackupProperties() throws GeneralPropertyException { // Use the metadata to extract configurable properties from the // overall properties list, and store those properties. try { SortedProperties backupProperties = new SortedProperties(mainBackupFilePath); // Populate the backup properties for main metacat properties using // the associated metadata file PropertiesMetaData mainMetadata = new PropertiesMetaData(mainMetadataFilePath); Map mainKeyMap = mainMetadata.getProperties(); Set mainKeySet = mainKeyMap.keySet(); for (String propertyKey : mainKeySet) { // don't backup passwords MetaDataProperty metaData = mainKeyMap.get(propertyKey); if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) { backupProperties.addProperty(propertyKey, getProperty(propertyKey)); } } // store the properties to file backupProperties.store(); mainBackupProperties = new SortedProperties(mainBackupFilePath); mainBackupProperties.load(); } catch (TransformerException te) { throw new GeneralPropertyException("Could not transform backup properties xml: " + te.getMessage()); } catch (IOException ioe) { throw new GeneralPropertyException("Could not backup configurable properties: " + ioe.getMessage()); } } /** * Writes out backup configurable properties to a file. */ public void persistAuthBackupProperties(ServletContext servletContext) throws GeneralPropertyException { // Use the metadata to extract configurable properties from the // overall properties list, and store those properties. try { SortedProperties backupProperties = new SortedProperties(authBackupFilePath); // Populate the backup properties for auth properties using // the associated metadata file PropertiesMetaData authMetadata = new PropertiesMetaData(authMetadataFilePath); Map authKeyMap = authMetadata.getProperties(); Set authKeySet = authKeyMap.keySet(); for (String propertyKey : authKeySet) { // don't backup passwords MetaDataProperty metaData = authKeyMap.get(propertyKey); if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) { backupProperties.addProperty(propertyKey, getProperty(propertyKey)); } } // store the properties to file backupProperties.store(); authBackupProperties = new SortedProperties(authBackupFilePath); authBackupProperties.load(); } catch (TransformerException te) { throw new GeneralPropertyException("Could not transform backup properties xml: " + te.getMessage()); } catch (IOException ioe) { throw new GeneralPropertyException("Could not backup configurable properties: " + ioe.getMessage()); } } /** * Reports whether properties are fully configured. * * @return a boolean that is true if properties are not unconfigured and * false otherwise */ public boolean arePropertiesConfigured() throws GeneralPropertyException { String propertiesConfigured = getProperty("configutil.propertiesConfigured"); if (propertiesConfigured != null && !propertiesConfigured.equals(UNCONFIGURED)) { return true; } return false; } /** * Determine if the system is configured to bypass configuration. If so, the * system will look for backup configuration files at startup time and use * those to configure metacat. The bypass options should only be set by * developers. Production code should never bypass confguration. * * @return true if dev.runConfiguration is set to true in metacat.properties * and we have not already checked for bypass, false otherwise. */ public boolean doBypass() throws PropertyNotFoundException { // We only want to go through the check once to see if we want to // bypass the configuration. We don't want to run through all of // this every time we hit metacat. if (bypassAlreadyChecked) { logMetacat.debug("bypassConfiguration not performing full bypass check. Bypass set to false"); return false; } // check how dev.runConfiguration is set in metacat.properties String strRunConfiguration = getProperty("dev.runConfiguration"); boolean runConfiguration = Boolean.parseBoolean(strRunConfiguration); logMetacat.debug("bypassConfiguration: dev.runConfiguration property set to: " + strRunConfiguration); // if the dev.runConfiguration is true, return false here. if (runConfiguration) { bypassAlreadyChecked = true; return false; } return true; } /** * Reports whether the metacat configuration utility should be run. * Returns false if * -- dev.runConfiguration=false and * -- backup properties file exists * Note that dev.runConfiguration should only be set to false when * reinstalling the same version of the application in developement. * * @return a boolean that is false if dev.runConfiguration is false and * the backup properties file exists. */ public void bypassConfiguration() throws GeneralPropertyException { try { boolean doBypass = doBypass(); if (!doBypass) { throw new GeneralPropertyException( "Attempting to do bypass when system is not configured for it."); } // The system is bypassing the configuration utility. We need to // get the backup properties and replace existing properties with // backup values. We do this for main and org properties. logMetacat.debug("bypassConfiguration: setting main backup properties."); SortedProperties mainBackupProperties = getMainBackupProperties(); Vector backupPropertyNames = mainBackupProperties.getPropertyNames(); for (String backupPropertyName : backupPropertyNames) { String value = mainBackupProperties.getProperty(backupPropertyName); setPropertyNoPersist(backupPropertyName, value); } logMetacat.debug("bypassConfiguration: setting auth backup properties."); SortedProperties authBackupProperties = getAuthBackupProperties(); Vector authBackupPropertyNames = authBackupProperties.getPropertyNames(); for (String authBackupPropertyName : authBackupPropertyNames) { String value = authBackupProperties.getProperty(authBackupPropertyName); setPropertyNoPersist(authBackupPropertyName, value); } logMetacat.debug("bypassConfiguration: setting configutil sections to true."); setPropertyNoPersist("configutil.propertiesConfigured", "true"); setPropertyNoPersist("configutil.authConfigured", "true"); setPropertyNoPersist("configutil.skinsConfigured", "true"); setPropertyNoPersist("configutil.databaseConfigured", "true"); setPropertyNoPersist("configutil.geoserverConfigured", "bypassed"); persistProperties(); } catch (PropertyNotFoundException pnfe) { logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage()); } catch (GeneralPropertyException gpe) { logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage()); } bypassAlreadyChecked = true; } /** * Take input from the user in an HTTP request about an property to be changed * and update the metacat property file with that new value if it has * changed from the value that was originally set. * * @param request * that was generated by the user * @param response * to send output back to the user * @param propertyName * the name of the property to be checked and set */ public boolean checkAndSetProperty(HttpServletRequest request, String propertyName) throws GeneralPropertyException { boolean changed = false; String value = getProperty(propertyName); String newValue = request.getParameter(propertyName); if (newValue != null && !newValue.trim().equals(value)) { setPropertyNoPersist(propertyName, newValue.trim()); changed = true; } return changed; } }