org.wisdom.configuration.ApplicationConfigurationImpl Maven / Gradle / Ivy
The newest version!
/*
* #%L
* Wisdom-Framework
* %%
* Copyright (C) 2013 - 2014 Wisdom Framework
* %%
* 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.
* #L%
*/
package org.wisdom.configuration;
import com.typesafe.config.*;
import org.apache.felix.ipojo.annotations.*;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.ow2.chameleon.core.services.AbstractDeployer;
import org.ow2.chameleon.core.services.Deployer;
import org.ow2.chameleon.core.services.Watcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wisdom.api.configuration.Configuration;
import org.wisdom.api.content.ParameterFactories;
import java.io.File;
import java.util.*;
/**
* Implementation of the configuration service reading application/conf and an external (optional) property.
*/
@Component
@Provides
@Instantiate
public class ApplicationConfigurationImpl extends ConfigurationImpl implements org.wisdom.api.configuration
.ApplicationConfiguration {
public static final String APPLICATION_CONFIGURATION = "application.configuration";
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationConfigurationImpl.class);
private final Mode mode;
private final File baseDirectory;
private static final String APPMODE = "application.mode";
private final BundleContext context;
private ServiceRegistration registration;
private Map> confRegistrations = new HashMap<>();
/**
* This service controller let unregisters the service when the configuration is reloaded.
*/
@ServiceController(value = false)
boolean controller;
/**
* The watcher service.
* It was injected in the constructor, but leads to issue when not available. This is an iPOJO bug.
*/
@Requires(optional = true, nullable = true)
Watcher watcher;
/**
* The configuration file.
*/
private final File configFile;
/**
* The configuration.
*/
private Config appConf;
/**
* Creates the application configuration object.
*
* @param converters the ParameterFactories service
* @param context the Bundle Context
*/
public ApplicationConfigurationImpl(@Requires ParameterFactories converters,
@Context BundleContext context) {
super(converters);
String location = reloadConfiguration();
this.context = context;
// Determine the mode.
String localMode = System.getProperty(APPMODE);
if (localMode == null) {
localMode = get(APPMODE);
}
if (localMode == null) {
this.mode = Mode.DEV;
} else {
this.mode = Mode.valueOf(localMode);
}
configFile = new File(location);
if (!configFile.isFile()) {
LOGGER.error("Cannot load the application configuration (" + location + ") - the file does not exist, " +
"Wisdom is using system properties only");
baseDirectory = new File("");
} else {
// The base directory is the parent of the parent
// getParentFile must be call on an absolute file, if not `null` is returned.
baseDirectory = configFile.getParentFile().getAbsoluteFile().getParentFile();
LOGGER.info("Configuration file : {}", configFile.getAbsoluteFile());
manageWatcher(context);
}
LOGGER.info("Base directory : {}", baseDirectory.getAbsoluteFile());
LOGGER.info("Wisdom running in " + this.mode.toString());
}
protected void manageWatcher(BundleContext context) {
if (context != null && (isDev() || getBooleanWithDefault("application.watch-configuration", false))
&& watcher != null) {
LOGGER.info("Enabling the watching of the configuration file");
watcher.add(configFile.getParentFile(), true);
registration = context.registerService(Deployer.class, new ConfigurationDeployer(), null);
}
}
@Validate
public void start() {
// Publish the service.
controller = true;
registerFirstLevelConfigurationAsServices();
}
/**
* Reloads the configuration file.
*
* @return the location of the file.
*/
private String reloadConfiguration() {
String location = System.getProperty(APPLICATION_CONFIGURATION);
if (location == null) {
location = "conf/application.conf";
}
Config configuration = loadConfiguration(location);
if (configuration == null) {
throw new IllegalStateException("Cannot load the application configuration (" + location + ") - Wisdom cannot " +
"work properly without such configuration");
}
setConfiguration(configuration);
return location;
}
/**
* Stops the service.
*/
@Invalidate
public void stop() {
unregisterConfigurationsExposedAsServices();
if (registration != null) {
registration.unregister();
registration = null;
try {
watcher.removeAndStopIfNeeded(configFile.getParentFile());
} catch (RuntimeException e) { //NOSONAR
// An exception can be thrown when the platform is shutting down.
// ignore it.
}
}
}
private Config loadConfiguration(String location) {
ConfigFactory.invalidateCaches();
if (location != null) {
File file = new File(location);
appConf = ConfigFactory.parseFileAnySyntax(file, ConfigParseOptions.defaults().setSyntax
(ConfigSyntax.CONF));
Properties properties = new Properties();
properties.put(APPLICATION_BASEDIR, file.getParentFile().getAbsoluteFile().getParentFile().getAbsolutePath());
return
ConfigFactory
.defaultOverrides()
.withFallback(appConf)
.withFallback(ConfigFactory.parseProperties(properties))
.resolve();
} else {
appConf = ConfigFactory.defaultOverrides();
return
ConfigFactory
.defaultOverrides()
.resolve();
}
}
/**
* {@inheritDoc}.
*/
@Override
public File getBaseDir() {
return baseDirectory;
}
/**
* Get a property as Integer or null if not there / or if the property is not an integer.
*
* @param key the key
* @return the property or {@literal null} if not there or property no integer
*/
@Override
public Integer getInteger(String key) {
Integer r = super.getInteger(key);
if (r == null) {
LOGGER.error(ERROR_NOSUCHKEY + key + "\"");
return null;
}
return r;
}
/**
* @param key the key
* @return the property or {@code null} if not there or if the property is not a boolean.
*/
@Override
public Boolean getBoolean(String key) {
Boolean r = super.getBoolean(key);
if (r == null) {
LOGGER.error(ERROR_NOSUCHKEY + key + "\"");
return null; //NOSONAR returning null to denotes that the property is not present.
}
return r;
}
/**
* @param key the key
* @return the property or {@code null} if not there or if the property is not a long.
*/
@Override
public Long getLong(String key) {
Long r = super.getLong(key);
if (r == null) {
LOGGER.error(ERROR_NOSUCHKEY + key + "\"");
return null;
}
return r;
}
/**
* Whether we are in dev mode.
*
* @return {@code true} if we are in dev mode
*/
@Override
public boolean isDev() {
return mode == Mode.DEV;
}
/**
* Whether we are in test mode.
*
* @return {@code true} if we are in test mode
*/
@Override
public boolean isTest() {
return mode == Mode.TEST;
}
/**
* Whether we are in prod mode.
*
* @return {@code true} if we are in prod mode
*/
@Override
public boolean isProd() {
return mode == Mode.PROD;
}
/**
* Get a File property or a default value when property cannot be found in
* any configuration file.
* The file object is constructed using new File(basedir, value)
.
*
* @param key the key
* @param file the default file
* @return the file object
*/
@Override
public File getFileWithDefault(String key, String file) {
String value = get(key);
if (value == null) {
return new File(baseDirectory, file);
} else {
return new File(baseDirectory, value);
}
}
/**
* Get a File property or a default value when property cannot be found in
* any configuration file.
* The file object is constructed using new File(basedir, value)
.
*
* @param key the key
* @param file the default file
* @return the file object
*/
@Override
public File getFileWithDefault(String key, File file) {
String value = get(key);
if (value == null) {
return file;
} else {
return new File(baseDirectory, value);
}
}
private class ConfigurationDeployer extends AbstractDeployer {
/**
* Checks that the file is the configuration file.
*
* @param file the file
* @return {@literal true} if the given file is the configuration file.
*/
@Override
public boolean accept(File file) {
return file.getAbsoluteFile().equals(configFile.getAbsoluteFile());
}
/**
* The configuration file is updated.
*
* @param file the configuration file
*/
@Override
public void onFileChange(File file) {
unregisterConfigurationsExposedAsServices();
controller = false;
reloadConfiguration();
controller = true;
registerFirstLevelConfigurationAsServices();
}
}
protected void unregisterConfigurationsExposedAsServices() {
// Unregister all services if not done yet
for (ServiceRegistration conf : confRegistrations.values()) {
conf.unregister();
}
confRegistrations.clear();
}
private void registerFirstLevelConfigurationAsServices() {
// Registers configuration
if (appConf == null) {
return;
}
for (Map.Entry entry : appConf.root().entrySet()) {
if (entry.getValue().valueType() == ConfigValueType.OBJECT) {
// Register it.
Dictionary properties = new Hashtable<>();
properties.put("configuration.name", entry.getKey());
properties.put("configuration.path", entry.getKey());
final ConfigurationImpl cf = new
ConfigurationImpl(converters, appConf.getConfig(entry.getKey()));
ServiceRegistration reg = context.registerService(Configuration.class, cf,
properties);
confRegistrations.put(cf, reg);
}
}
}
}