/** * Copyright 2006 by Know-Center, Graz, Austria * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. * * $Id: SettingsReader.java,v 1.6 2006/10/31 08:06:36 wprinz Exp $ */ package at.knowcenter.wag.egov.egiz.cfg; import iaik.asn1.ObjectID; import iaik.security.ecc.provider.ECCProvider; import iaik.security.provider.IAIK; import iaik.utils.RFC2253NameParser; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.security.Security; import java.util.ArrayList; import java.util.Enumeration; import java.util.Properties; import java.util.Vector; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrSubstitutor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import at.gv.egiz.pdfas.api.commons.Constants; import at.gv.egiz.pdfas.api.exceptions.ConfigUtilsException; import at.gv.egiz.pdfas.utils.ConfigUtils; import at.gv.egiz.pdfas.utils.TempDirHelper; import at.knowcenter.wag.egov.egiz.PdfAS; import at.knowcenter.wag.egov.egiz.exceptions.SettingNotFoundException; import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; import at.knowcenter.wag.egov.egiz.pdf.Utils; import at.knowcenter.wag.egov.egiz.tools.FileHelper; /** * The SettingsReader reads the settings.txt file. The * settings.txt is a simple java property file that collects all * parameters used in different modules. * * The SettingsReader provides methods to get the property keys and the * corresponding values. The keys could be defined as combinations of single * keys. Therefore it is possible to combine differen classes of keys. An * example could be: * *
 *     
 *                        #SettingNotFoundException
 *                        error.code.100=Interner Fehler
 *                        error.code.101=Die Konfigurationsdatei konnte nicht geladen werden
 *                        
 *                        #PDFDocumentException
 *                        error.code.200=Das Dokument konnte nicht geladen werden
 *                        
 *                        #SignatureException
 *                        error.code.300=Die Signatur ist ungültig
 *                        
 *                        #NormalizeException
 *                        error.code.400=Die angegebene Version ist nicht bekannt
 *                       
 *                        normalizer.version=V01

 *      
 * 
* * The internal representation of the example above is: * *
 *     

 *                        .error|
 *                              |.code|
 *                              |     |.200=Das Dokument konnte nicht geladen werden
 *                              |     |.100=Interner Fehler
 *                              |     |.400=Die angegebene Version ist nicht bekannt
 *                              |     |.101=Die Konfigurationsdatei konnte nicht geladen werden
 *                              |     |.300=Die Signatur ist ungueltig
 *                       .normalizer|
 *                                  |.version=V01      
 *      
 * 
* * @author wlackner */ public class SettingsReader implements Serializable { /** * SVUID. */ private static final long serialVersionUID = -8754114172766023454L; /** * The system File separator char */ private static final String FILE_SEP = System.getProperty("file.separator"); // /** // * The system temp file path // */ // private static final String TEMP_FILE_PATH = // System.getProperty("java.io.tmpdir"); // /** // * The home path of the tomcat webaplication // */ // private static final String CATALINA_HOME = System.getProperty("catalina.home"); // /** // * The default application name used in templates, settings, jsp's etc. // */ // private static final String APPL_NAME = "pdf-as"; // private static final String APPL_NAME = "egiz"; /** * The config file path postfix */ private static final String CFG = "cfg"; /** * The file path postfix where certificates are stored */ private static final String CERT = "certificates"; /** * pdf-as internal properties resource path */ private static final String PDF_AS_PROP_RESOURCE = "/config/pdf-as.properties"; /** * internal help file */ private static final String HELP_TEXT_PROP_RESOURCE = "/config/help_text.properties"; public static final boolean REGISTER_IAIK_PROVIDERS_ON_DEFAULT = true; // /** // * The web application path // */ // private static final String WEB_APPL_DIR = "webapps" + FILE_SEP + APPL_NAME + FILE_SEP; /** * The path of the resources repository. * *

* This usually contains sub directories for the templates, the configuration * files, etc. *

*/ public static String RESOURCES_PATH = null; /** * The path for temporary files. */ public static String TMP_PATH = null; /** * The path of the configuration directory. */ public static String CONFIG_PATH = null; /** * The path of the certificated directory. */ public static String CERT_PATH = null; // /** // * The application config path for the command line tool // */ // public static final String APPL_CONFIG_PATH = USER_DIR + FILE_SEP + CFG + // FILE_SEP; // // /** // * The application config path for the web application // */ // public static final String WEB_CONFIG_PATH = CATALINA_HOME + FILE_SEP + // WEB_APPL_DIR + CFG + FILE_SEP; // // /** // * The certificates path for the command line tool // */ // public static final String APPL_CERT_PATH = USER_DIR + FILE_SEP + CERT + // FILE_SEP; // // /** // * The certificates path for the cweb application // */ // public static final String WEB_CERT_PATH = CATALINA_HOME + FILE_SEP + // WEB_APPL_DIR + CERT + FILE_SEP; /** * The name of the default configuration file. The definition syntax is the * java property config syntax. */ public static final String CONFIG_FILE_DEFAULT_NAME = "config.properties"; /** * The name of the help text configuration file. The definition syntax is the * java property config syntax. */ // public static final String HELP_TEXT_FILE_DEFAULT_NAME = "help_text.properties"; /** * The java properties from the settings file. */ private Properties properties_ = null; /** * The settings reader instance. Used to make the class singleton. */ private static SettingsReader instance_ = null; /** * The reference to the settings file. */ private static String settingsFile_ = null; /** * The reference to the property representation of the settings file. */ private PropertyTree pTree_ = new PropertyTree(); /** * The log. */ private static final Log logger_ = LogFactory.getLog(SettingsReader.class); private static final String INTERNAL_RESOURCE_PATH = "/config/"; /** * Make this constructor private. Use the method * {@link SettingsReader#getInstance()}to get an instance from this class. * The only cause to do this is that the definition file should only be read * once while getting often this instance. The method throws an IOException if * the settings file could not be read. * * @param settingsFile * load this file, if the settingsFile == null the * default settings ({@link SettingsReader#CONFIG_FILE_DEFAULT_NAME}) * file is used * @throws SettingsException * if the settings file could not be read */ private SettingsReader(String settingsFile) throws SettingsException { try { String cfg_path = CONFIG_PATH; properties_ = new Properties(); if (settingsFile == null) { settingsFile = cfg_path + CONFIG_FILE_DEFAULT_NAME; } settingsFile_ = settingsFile; if (logger_.isInfoEnabled()) { File file = new File(settingsFile_); logger_.debug("load Settings:" + file.getAbsolutePath()); // Properties sys_prop = System.getProperties(); // Enumeration prop_keys = sys_prop.propertyNames(); // while (prop_keys.hasMoreElements()) { // String key = (String) prop_keys.nextElement(); // String value = sys_prop.getProperty(key); // logger_.info(key + "=" + value); // } } FileInputStream sfs = new FileInputStream(settingsFile_); properties_.load(sfs); // dferbas override with system props properties_.load(SettingsReader.class.getResourceAsStream(PDF_AS_PROP_RESOURCE)); Properties help_prop = new Properties(); // FileInputStream hfs = new FileInputStream(cfg_path + HELP_TEXT_FILE_DEFAULT_NAME); // help_prop.load(hfs); help_prop.load(SettingsReader.class.getResourceAsStream(HELP_TEXT_PROP_RESOURCE)); // load properties from current package! // properties_.load(getClass().getResourceAsStream(settingsFile_)); Enumeration prop_keys = properties_.propertyNames(); while (prop_keys.hasMoreElements()) { String key = (String) prop_keys.nextElement(); String value = properties_.getProperty(key); pTree_.setKeyValue(key, value); } prop_keys = help_prop.propertyNames(); while (prop_keys.hasMoreElements()) { String key = (String) prop_keys.nextElement(); String value = help_prop.getProperty(key); properties_.setProperty(key, value); pTree_.setKeyValue(key, value); } } catch (IOException e) { throw new SettingsException("Couldn't load settings from file " + settingsFile, e); } } /** * This method returns an synchronized instance of this class. The settings * file is read only once using this class. This method returns the instance * holding the definitions of the default settings file. Default file: * {@link SettingsReader#CONFIG_FILE_DEFAULT_NAME}: "settings.txt". * Note: IAIK JCE and IAIK ECC security providers are automatically registered. * * @return an instance of the SettingsReader * @throws SettingsException * if the default settings file could not be read */ public synchronized static SettingsReader getInstance() throws SettingsException { return getInstance(null); } /** * Reloads the Settings file. * *

* Subsequent calls to getInstance will return the new settings. * Note: IAIK JCE and IAIK ECC security providers are automatically registered. *

* * @throws SettingsException f.e. */ public synchronized static void createInstance() throws SettingsException { instance_ = null; getInstance(); } /** * Reloads the Settings file. * *

* Subsequent calls to getInstance will return the new settings. *

* @param registerProvider true: automatically registers IAIK JCE and ECC Provider; * false: providers will NOT be automatically registered, providers * needed have to be registered by the API user * @throws SettingsException f.e. */ public synchronized static void createInstance(boolean registerProvider) throws SettingsException { instance_ = null; getInstance(null, registerProvider); } /** * This method returns an synchronized instance of this class. The settings * file is read only once using this class. This method returns the instance * holding the definitions of the settingsFile. If the input param * settingsFile == null the default settings file will be load. * Default file: {@link SettingsReader#CONFIG_FILE_DEFAULT_NAME}: * "settings.txt" * * If an instance of this class exist, the input param is ignored! The * SettingsReader is singleton and therefore the first * {@link SettingsReader#getInstance()}defines the settings file that has to * be loaded. This means changes between a application lifecyle can not be * done! * * @param settingsFile * the settings file that should be load. * @param registerProvider true: automatically registers IAIK JCE and ECC Provider; * false: providers will NOT be automatically registered, providers * needed have to be registered by the API user * @return an instance of the SettingsReader * @throws SettingsException * if the settings file could not be read */ private synchronized static SettingsReader getInstance(String settingsFile, boolean registerProvider) throws SettingsException { if (instance_ == null) { int length = Utils.max(new int[] { RESOURCES_PATH.length(), TMP_PATH.length(), CONFIG_PATH.length(), CERT_PATH.length() }); logger_.info(StringUtils.repeat("*", length + 25)); logger_.info(" resources path = \"" + RESOURCES_PATH + "\""); logger_.info(" configuration path = \"" + CONFIG_PATH + "\""); logger_.info(" certstore path = \"" + CERT_PATH + "\""); logger_.info(" temporary path = \"" + TMP_PATH + "\""); logger_.debug(" file.encoding = \"" + System.getProperty("file.encoding") + "\""); logger_.info(StringUtils.repeat("*", length + 25)); if (registerProvider) { IAIK.addAsProvider(); ECCProvider.addAsProvider(); } else { if (Security.getProvider("IAIK") == null) { logger_.debug("Default IAIK JCE provider not registered."); } else { logger_.debug("IAIK JCE provider already registered."); } if (Security.getProvider("IAIK_ECC") == null) { logger_.debug("Default IAIK ECC provider not registered."); } else { logger_.debug("IAIK ECC provider already registered."); } } // Does not conform with PKIX, but is used by belgium citizen card // log.info("Registering RDN \"SERIALNUMBER\" as " + ObjectID.serialNumber + "."); RFC2253NameParser.register("SERIALNUMBER", ObjectID.serialNumber); instance_ = new SettingsReader(settingsFile); } return instance_; } /** * This method returns an synchronized instance of this class. The settings * file is read only once using this class. This method returns the instance * holding the definitions of the settingsFile. If the input param * settingsFile == null the default settings file will be load. * Default file: {@link SettingsReader#CONFIG_FILE_DEFAULT_NAME}: * "settings.txt". * Note: IAIK JCE and IAIK ECC security providers are automatically registered. * * If an instance of this class exist, the input param is ignored! The * SettingsReader is singleton and therefore the first * {@link SettingsReader#getInstance()}defines the settings file that has to * be loaded. This means changes between a application lifecyle can not be * done! * * @param settingsFile * the settings file that should be load. * @return an instance of the SettingsReader * @throws SettingsException * if the settings file could not be read */ private static SettingsReader getInstance(String settingsFile) throws SettingsException { return getInstance(settingsFile, REGISTER_IAIK_PROVIDERS_ON_DEFAULT); } /** * This method returns a property value to the corresponding key. If the key * is not found in the property file a SettingNotFoundException is thrown. * * @param key * get the value for that key in the property file * @return the value of the property key. * @throws SettingNotFoundException * ErrorCode: 100 */ public String getSetting(String key) throws SettingNotFoundException { String result = OverridePropertyHolder.getProperty(key); if (result == null) { result = properties_.getProperty(key); } if (result == null) { String log_message = "Configuration key not found: '" + key + "'! Check '" + settingsFile_ + "' file."; if (logger_.isWarnEnabled()) { logger_.warn(log_message); } SettingNotFoundException snf = new SettingNotFoundException(log_message); throw snf; } return result; } // TODO in the next change request, the Setting system will be refactored // this is just for testing purposes. public void setSetting(String key, String value) { properties_.setProperty(key, value); } /** * Relocates the relative file. * * @param file * The relative file. * @return Returns the usable file. */ public static String relocateFile(String file) { // if (isWeb()) // { // return CATALINA_HOME + FILE_SEP + WEB_APPL_DIR + file; // } // // return file; return RESOURCES_PATH + file; } /** * This method returns a property value to the corresponding key. If the key * is not found in the property file the input param defaultValue is returned. * * @param key * get the value for that key in the property file * @param defaultValue * the default value if the key is not found * @return the value of the property key */ public String getSetting(String key, String defaultValue) { String result = properties_.getProperty(key); if (result == null) { result = defaultValue; } // if (logger_.isDebugEnabled()) // { // logger_.debug("Get Property:" + key + "=" + result); // } return result; } /** * This method returns a property value to the corresponding key. If the key * is not found in the property file the input param defaultKey is searched. * If the default key is not found the input param defaultValue is returned. * * @param primaryKey * get the value for that key in the property file * @param defaultKey * the default key that should be searched if the primaryKey is not * found * @param defaultValue * the default value if the defaultKey is not found * @return the value of the property key */ public String getSetting(String primaryKey, String defaultKey, String defaultValue) { String key = primaryKey; String result = properties_.getProperty(key); if (result == null) { key = defaultKey; result = properties_.getProperty(key); if (result == null) { result = defaultValue; } } // if (logger_.isDebugEnabled()) // { // logger_.debug("Get Property:" + key + "=" + result); // } return result; } /** * This method returns an array of keys in the same hierarchy of the * keyPrefix. The method search all keys in the property file that has the * keyPrefix as leading substring. The Object[] collects all * sub keys without the keyPrefix. * * @param keyPrefix * to search for sub keys * @return alls keys starting with the keyPrefix */ public Vector getSettingKeys(String keyPrefix) { Vector keys = new Vector(); Enumeration names = properties_.propertyNames(); while (names.hasMoreElements()) { String full_name = (String) names.nextElement(); if (full_name.indexOf(keyPrefix) == 0) { keys.add(full_name.substring(keyPrefix.length() + 1)); } } return keys; } /** * If a property value is number (interger) this method extracts the value and * convert it to an int. If the key ist not found or the conversion fails, the * defaultValue is returned. * * @param key * get the value for that key in the property file * @param defaultValue * the default value if the key is not found * @return the int value of the property key */ public int getIntSetting(String key, int defaultValue) { int int_property = defaultValue; String value = null; try { value = getSetting(key); int_property = Integer.parseInt(value); } catch (NumberFormatException e) { if (logger_.isWarnEnabled()) { logger_.warn("Can not convert " + value + " to int.", e); } } catch (SettingNotFoundException e) { if (logger_.isWarnEnabled()) { logger_.warn("Setting " + key + " not found, return default value:" + defaultValue, e); } } return int_property; } /** * This method returns an array of sub keys (children references) of the key. * The method is a wrapper calling the method * {@link PropertyTree#getKeys(String key)}. * * @param key * get all sub keys for that key in the property file * @return an list of sub keys (type String) * @see PropertyTree */ public ArrayList getKeys(String key) { return pTree_.getKeys(key); } /** * This method returns a the first value from a key. This means the method * search in the PropertyTree representation of the config file. The * PropertyTree class can overload key value paires. But the config file can * not overload keys. If a key is defined more than one times the last * definition is stored it the property list. The method is a wrapper calling * the method {@link PropertyTree#getFirstValue(String key)}. * * @param key * get the value for that key in the property file * @return the value of the property key * @see PropertyTree */ public String getValueFromKey(String key) { String value = OverridePropertyHolder.getProperty(key); if (value == null) { value = pTree_.getFirstValue(key); } return value; } /** * This method returns the PropertyTree representation of the configuration * file. * * @return Returns the pTree. * @see PropertyTree */ public PropertyTree getPTree() { return pTree_; } /** * Reads internal resource as string. * @param relativePath * @return null in case of error */ public String readInternalResourceAsString(String relativePath) { // return readAsString(getInternalResource(relativePath)); return FileHelper.readFromInputStream(getInternalResource(relativePath)); } /** * Get resource as stream, relative to internal resource path {@value #INTERNAL_RESOURCE_PATH} * * @param relativePath * @return */ public InputStream getInternalResource(String relativePath) { // kill starting "." and "./" in resource path relativePath = StringUtils.removeStart(relativePath, "."); relativePath = StringUtils.removeStart(relativePath, "/"); String streamURI = INTERNAL_RESOURCE_PATH + relativePath; logger_.trace("Trying to get stream from \"" + streamURI + "\"."); InputStream stream = this.getClass().getResourceAsStream(streamURI); if (stream == null) { logger_.trace("Could not get stream."); } else { logger_.trace("Got stream."); } return stream; } /** * Read resource as utf8 string. * @param is * @return null in case of error */ /* public String readAsString(InputStream is) { if (is == null) return null; try { return IOUtils.toString(is, "utf-8"); } catch (IOException e) { logger_.info("error reading stream to string ", e); } return null; } */ // /** // * This method checks the application context. // * // * @return true if the application is running in a webinterface, false // * otherwise // */ // public static boolean isWeb() // { // return CATALINA_HOME != null; // } /** * Assembles the File of the temporary directory without checking if it really * exists. * @see TempDirHelper#assembleTemporaryDirectoryFile() */ protected static File assembleTemporaryDirectoryFile() { return TempDirHelper.assembleTemporaryDirectoryFile(); } /** * Returns the directory where temporary files should be stored. * *

* If the directory doesn't exist, it is created. *

* * @return Returns the directory where temporary files should be stored. * @see TempDirHelper#getTemporaryDirectory() */ public static File getTemporaryDirectory() { return TempDirHelper.getTemporaryDirectory(); } /** * Deletes all files in the temporary directory, if it exists. * *

* This should be used to clear temporary files when the application shuts * down. *

* @see TempDirHelper#clearTemporaryDirectory() */ public static void clearTemporaryDirectory() { TempDirHelper.clearTemporaryDirectory(); } public static synchronized void initialize(String configdir, String tmpdir) { String defaultConfigDeployedTo = null; // resolve work directory // configuration explicitely given ? if (configdir == null) { // configuration via system property ? logger_.debug("No configuration directory given. Looking for system property \"" + Constants.CONFIG_DIR_SYSTEM_PROPERTY + "\"."); configdir = System.getProperty(Constants.CONFIG_DIR_SYSTEM_PROPERTY); if (configdir == null) { // configuration via user's home directory ? logger_.debug("System property not set. Trying to locate configuration within the user's home directory."); String userHome = System.getProperty("user.home"); if (userHome == null || userHome.length() == 0) { throw new RuntimeException("Unable to resolve user's home directory."); } configdir = ConfigUtils.assertFileSeparator(userHome) + Constants.USERHOME_CONFIG_FOLDER; try { defaultConfigDeployedTo = ConfigUtils.deployDefaultConfiguration(configdir, false); } catch (ConfigUtilsException e) { throw new RuntimeException(e); } if (defaultConfigDeployedTo != null) { logger_.info("** Default configuration successfully deployed to \"" + defaultConfigDeployedTo + "\" **"); } else { logger_.debug("Default configuration has NOT been deployed. Maybe the configuration already exists."); } } else { logger_.debug("Configuration set by system property."); if (tmpdir == null) { tmpdir = configdir; } } } else { logger_.debug("Configuration path explicitely set."); } File configdirFile = new File(StrSubstitutor.replaceSystemProperties(configdir)); try { configdir = ConfigUtils.assertFileSeparator(configdirFile.getCanonicalPath()); } catch (IOException e) { configdir = ConfigUtils.assertFileSeparator(configdirFile.getPath()); } if (!configdirFile.isDirectory()) { throw new IllegalArgumentException("The config directory \"" + configdir + "\" does not exist or is not a directory."); } // resolve temporary dir if (tmpdir == null) { logger_.debug("Temporary directory not explicitely set. Looking for user's temp directory."); tmpdir = System.getProperty("java.io.tmpdir"); if (tmpdir == null) { logger_.debug("Unable to resolve user's temporary directory. Assuming temporary directory located within config dir."); tmpdir = configdir; } } else { logger_.debug("Temporary directory explicitely set."); } File tmpdirFile = new File(StrSubstitutor.replaceSystemProperties(ConfigUtils.assertFileSeparator(tmpdir) + Constants.TEMP_DIR_NAME)); try { tmpdir = ConfigUtils.assertFileSeparator(tmpdirFile.getCanonicalPath()); } catch (IOException e) { tmpdir = ConfigUtils.assertFileSeparator(tmpdirFile.getPath()); } RESOURCES_PATH = configdir; TMP_PATH = tmpdir; CONFIG_PATH = RESOURCES_PATH + CFG + FILE_SEP; CERT_PATH = RESOURCES_PATH + CERT + FILE_SEP; // ConfigUtils.printConfigInfo(logger_); if (defaultConfigDeployedTo != null) { logger_.debug("** Default configuration successfully deployed to \"" + defaultConfigDeployedTo + "\" **"); } logger_.debug("Setting system property \"" + Constants.CONFIG_DIR_SYSTEM_PROPERTY + "\" to \"" + configdirFile.getPath() + "\"."); System.setProperty(Constants.CONFIG_DIR_SYSTEM_PROPERTY, configdirFile.getPath()); } public static void initialize(String base_dir) { initialize(base_dir, null); } /** * Initializes the paths of the SettingsReader for web application usage. * * @param base_dir * The base directory of this web application. E.g. * TOMCAT_HOME/webapps/pdf-as */ public static void initializeForWeb(String base_dir) { initialize(base_dir, base_dir); } /** * Initializes the paths of the SettingsReader for commanline usage. */ public static void initializeForCommandLine() { initialize(null); } static { String versionString = "* PDF-AS library version " + PdfAS.PDFAS_VERSION + " *"; String paddingString = StringUtils.repeat("*", versionString.length()); logger_.info("PDF-AS info\n" + paddingString + "\n" + versionString + "\n" + paddingString); } public Properties getProperties() { return this.properties_; } }