/* * * * Copyright (c) 2016. David Sowerby * * * * 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. * */ package uk.q3c.krail.core.config; import com.google.inject.Inject; import com.google.inject.Singleton; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.HierarchicalINIConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.q3c.krail.core.eventbus.GlobalBusProvider; import uk.q3c.krail.core.i18n.DescriptionKey; import uk.q3c.krail.core.i18n.I18NKey; import uk.q3c.krail.core.i18n.LabelKey; import uk.q3c.krail.core.i18n.Translate; import uk.q3c.krail.core.services.AbstractService; import uk.q3c.krail.core.services.RelatedServicesExecutor; import uk.q3c.krail.core.services.Service; import uk.q3c.krail.util.ResourceUtils; import javax.annotation.concurrent.ThreadSafe; import java.io.File; import java.io.FileNotFoundException; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * This service provides a mechanism which can be used to manage the whole application configuration. It uses the * Apache * Commons Configuration library, and specifically a {@link CompositeConfiguration} so that the configuration can be * extended in any way the developer wishes. * <p/> * We have a preference for using HierarchicalINIConfiguration to provide a good level of human readability (when the * key value pairs are stored in a file) but any of the Apache {@link Configuration} implementations could be used. * <p/> * See the {@link Service} javadoc for more detail about Services * <p/> * Once this service has been started, access the configuration by injecting {@link InheritingConfiguration}. Note that * the configuration can be legitimately empty if no configuration non-default settings are required, and all calls for * configuration values provide a valid default. * <p/> * When this service is stopped the {@link InheritingConfiguration} is cleared. * * @author David Sowerby */ @Singleton @ThreadSafe public class DefaultApplicationConfigurationService extends AbstractService implements ApplicationConfigurationService { private static Logger log = LoggerFactory.getLogger(DefaultApplicationConfigurationService.class); private final ApplicationConfiguration configuration; private final Map<Integer, IniFileConfig> iniFiles; private ResourceUtils resourceUtils; @Inject protected DefaultApplicationConfigurationService(Translate translate, ApplicationConfiguration configuration, Map<Integer, IniFileConfig> iniFiles, GlobalBusProvider globalBusProvider, ResourceUtils resourceUtils, RelatedServicesExecutor servicesExecutor) { super(translate, globalBusProvider, servicesExecutor); this.configuration = configuration; this.iniFiles = iniFiles; this.resourceUtils = resourceUtils; setDescriptionKey(DescriptionKey.Application_Configuration_Service); } /** * The {@link #iniFiles} map is processed in ascending key order. If a file does not exist or fails to load for any * reason, and it is not marked as optional in IniFileConfig, a {@link ConfigurationException} is thrown. If, * however, the file fails to load, and is optional, no exception is raised. * * @throws ConfigurationException * if an error occurs while loading a file */ @SuppressFBWarnings("EXS_EXCEPTION_SOFTENING_HAS_CHECKED") @Override protected void doStart() { Set<Integer> keySorter = new TreeSet<>(iniFiles.keySet()).descendingSet(); for (Integer k : keySorter) { IniFileConfig iniConfig = iniFiles.get(k); File file = new File(resourceUtils.configurationDirectory(), iniConfig.getFilename()); try { if (!file.exists()) { throw new FileNotFoundException(file.getAbsolutePath()); } HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(file); configuration.addConfiguration(config); log.debug("adding configuration from {} at index {}", file.getAbsolutePath(), k); } catch (Exception ce) { if (!iniConfig.isOptional()) { String msg = ("Configuration Service failed to start, unable to load required configuration file: {}"); log.error(msg, file.getAbsolutePath()); throw new ConfigurationException(msg + file.getAbsolutePath(), ce); } else { log.info("Optional configuration file not found at {}, but as it is optional, " + "" + "continuing without it", file); } } } log.info("Application Configuration Service started"); } /** * Clears the {@link ApplicationConfiguration} */ @Override protected void doStop() { configuration.clear(); } @Override public synchronized I18NKey getNameKey() { return LabelKey.Application_Configuration_Service; } }