package com.intuit.tank.vm.settings; /* * #%L * Intuit Tank Api * %% * Copyright (C) 2011 - 2015 Intuit Inc. * %% * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * #L% */ import java.io.File; import java.io.Serializable; import java.net.URL; import java.util.List; import javax.annotation.Nonnull; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.XMLConfiguration; import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy; import org.apache.commons.configuration.tree.ExpressionEngine; import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * BaseCommonsConfig * * @author dangleton */ public abstract class BaseCommonsXmlConfig implements Serializable { private static final long serialVersionUID = 1L; private static final Logger LOG = LogManager.getLogger(BaseCommonsXmlConfig.class); protected XMLConfiguration config; protected File configFile; /** * Constructor pulls file out of the jar or reads from disk and sets up refresh policy. * * @param expressionEngine * the expression engine to use. Null results in default expression engine */ protected void readConfig() { try { ExpressionEngine expressionEngine = new XPathExpressionEngine(); String configPath = getConfigName(); FileChangedReloadingStrategy reloadingStrategy = new FileChangedReloadingStrategy(); File dataDirConfigFile = new File(configPath); // LOG.info("Reading settings from " + dataDirConfigFile.getAbsolutePath()); if (!dataDirConfigFile.exists()) { // Load a default from the classpath: // Note: we don't let new XMLConfiguration() lookup the resource // url directly because it may not be able to find the desired // classloader to load the URL from. URL configResourceUrl = this.getClass().getClassLoader().getResource(configPath); if (configResourceUrl == null) { throw new RuntimeException("unable to load resource: " + configPath); } XMLConfiguration tmpConfig = new XMLConfiguration(configResourceUrl); // Copy over a default configuration since none exists: // Ensure data dir location exists: if (dataDirConfigFile.getParentFile() != null && !dataDirConfigFile.getParentFile().exists() && !dataDirConfigFile.getParentFile().mkdirs()) { throw new RuntimeException("could not create directories."); } tmpConfig.save(dataDirConfigFile); LOG.info("Saving settings file to " + dataDirConfigFile.getAbsolutePath()); } if (dataDirConfigFile.exists()) { config = new XMLConfiguration(dataDirConfigFile); } else { // extract from jar and write to throw new IllegalStateException("Config file does not exist or cannot be created"); } if (expressionEngine != null) { config.setExpressionEngine(expressionEngine); } configFile = dataDirConfigFile; // reload at most once per thirty seconds on configuration queries. config.setReloadingStrategy(reloadingStrategy); initConfig(config); } catch (ConfigurationException e) { LOG.error("Error reading settings file: " + e, e); throw new RuntimeException(e); } } public File getSourceConfigFile() { return configFile; } @SuppressWarnings("unchecked") public static HierarchicalConfiguration getChildConfigurationAt(HierarchicalConfiguration config, String key) { if (config == null) { return null; } List<HierarchicalConfiguration> configs = config.configurationsAt(key); if (configs.size() > 1) { LOG.warn("Child configuration with key " + key + " matches more than one node."); } else if (configs.size() == 0) { LOG.warn("Child configuration with key " + key + " has no entry in config file."); } return configs.size() != 0 ? configs.get(0) : null; } /** * @return the config */ protected XMLConfiguration getConfig() { checkReload(); return config; } /** * * @return the name of the config to fetch. e.g. SitesConfig.xml */ @Nonnull protected abstract String getConfigName(); /** * initialize the configuration form the passed in config. * * @param configuration * the configuration to initialize from */ protected abstract void initConfig(@Nonnull XMLConfiguration configuration); /** * checks if the config needs to be reloaded and calls initConfig on it. should be called if parsing of the config * is needed. */ protected synchronized void checkReload() { if (config == null) { readConfig(); } else if (config.getReloadingStrategy().reloadingRequired()) { config.reload(); initConfig(config); config.getReloadingStrategy().reloadingPerformed(); } } /** * checks if the config needs to be reloaded. */ public boolean needsReload() { return (config == null || config.getReloadingStrategy().reloadingRequired()); } }