/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* 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
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package org.codice.ddf.configuration;
import java.io.IOException;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The DDF Configuration Manager manages the DDF system settings. Some of these
* settings are displayed in the Web Admin Console's Configuration tab under the
* DDF System Settings configuration. Other settings are read-only, not
* displayed in the DDF System Settings configuration (but appear in other OSGi
* bundle configurations such as CXF). These read-only settings are included in
* the list of configuration settings pushed to registered listeners.
* <p>
* Registered listeners implement the ConfigurationWatcher interface and have
* these DDF configuration settings pushed to them when they come online (aka
* bind) and when one or more of the settings are changed in the Admin Console.
*/
public class ConfigurationManager {
/**
* Service PID to use to look up System Settings Configuration.
*/
public static final String PID = "ddf.platform.config";
// Constants for the DDF system settings appearing in the Admin Console
/**
* The directory where DDF is installed
*/
public static final String HOME_DIR = "homeDir";
/**
* The port number that CXF's underlying Jetty server is listening on, e.g.,
* 8181
*/
public static final String HTTP_PORT = "httpPort";
/**
* The context root for all DDF services, e.g., the /services portion of the
* http://hostname:8181/services URL
*/
public static final String SERVICES_CONTEXT_ROOT = "servicesContextRoot";
/**
* The hostname or IP address of the machine that DDF is running on
*/
public static final String HOST = "host";
/**
* The port number that DDF is listening on, e.g., 8181
*/
public static final String PORT = "port";
/**
* The protocol that DDF is using http/https
*/
public static final String PROTOCOL = "protocol";
/**
* Trust store to use for outgoing DDF connections
*/
public static final String TRUST_STORE = "trustStore";
/**
* Password associated with the trust store
*/
public static final String TRUST_STORE_PASSWORD = "trustStorePassword";
/**
* Key store to use for outgoing DDF connections
*/
public static final String KEY_STORE = "keyStore";
/**
* Password associated with the key store
*/
public static final String KEY_STORE_PASSWORD = "keyStorePassword";
/**
* The site name for this DDF instance
*/
public static final String SITE_NAME = "id";
/**
* The version of DDF currently running
*/
public static final String VERSION = "version";
/**
* The organization that this instance of DDF is running for
*/
public static final String ORGANIZATION = "organization";
/**
* Site (email) contact
*/
public static final String CONTACT = "contact";
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationManager.class);
// Constants for the read-only DDF system settings
private static final String DDF_HOME_ENVIRONMENT_VARIABLE = "DDF_HOME";
private static final String SSL_KEYSTORE_JAVA_PROPERTY = "javax.net.ssl.keyStore";
private static final String SSL_KEYSTORE_PASSWORD_JAVA_PROPERTY =
"javax.net.ssl.keyStorePassword";
private static final String SSL_TRUSTSTORE_JAVA_PROPERTY = "javax.net.ssl.trustStore";
private static final String SSL_TRUSTSTORE_PASSWORD_JAVA_PROPERTY =
"javax.net.ssl.trustStorePassword";
/**
* List of DdfManagedServices to push the DDF system settings to.
*/
protected List<ConfigurationWatcher> services;
/**
* The map of DDF system settings, including the read-only settings.
*/
protected Map<String, String> configuration;
private static Map<String, String> propertyMapping = new HashMap<>();
static {
propertyMapping.put(PROTOCOL, SystemBaseUrl.PROTOCOL);
propertyMapping.put(HOST, SystemBaseUrl.HOST);
propertyMapping.put(PORT, SystemBaseUrl.PORT);
propertyMapping.put(SITE_NAME, SystemInfo.SITE_NAME);
propertyMapping.put(CONTACT, SystemInfo.SITE_CONTACT);
propertyMapping.put(ORGANIZATION, SystemInfo.ORGANIZATION);
propertyMapping.put(VERSION, SystemInfo.VERSION);
}
/**
* The map of DDF system settings that are read-only, i.e., they are set in
* OSGi system bundles, not displayed in Admin Console's DDF System Settings
* configuration, but are pushed out in the configuration settings to
* ConfigurationWatchers.
*/
protected Map<String, String> readOnlySettings;
protected ConfigurationAdmin configurationAdmin;
/**
* The initial configuration values from blueprint.
*/
private Map<String, String> configurationProperties = new HashMap<>();
/**
* Constructs the list of DDF system Settings (read-only and configurable
* settings) to be pushed to registered ConfigurationWatchers.
*
* @param services the list of watchers of changes to the DDF System Settings
* @param configurationAdmin the OSGi Configuration Admin service handle
*/
public ConfigurationManager(List<ConfigurationWatcher> services,
ConfigurationAdmin configurationAdmin) {
LOGGER.debug("ENTERING: ctor");
this.services = services;
this.configurationAdmin = configurationAdmin;
this.readOnlySettings = new HashMap<>();
if (System.getenv(DDF_HOME_ENVIRONMENT_VARIABLE) != null) {
readOnlySettings.put(HOME_DIR, System.getenv(DDF_HOME_ENVIRONMENT_VARIABLE));
} else {
readOnlySettings.put(HOME_DIR, System.getProperty("user.dir"));
}
// Add the system properties
configurationProperties.putAll(getSystemProperties());
readOnlySettings.put(KEY_STORE, System.getProperty(SSL_KEYSTORE_JAVA_PROPERTY));
readOnlySettings.put(KEY_STORE_PASSWORD,
System.getProperty(SSL_KEYSTORE_PASSWORD_JAVA_PROPERTY));
readOnlySettings.put(TRUST_STORE, System.getProperty(SSL_TRUSTSTORE_JAVA_PROPERTY));
readOnlySettings.put(TRUST_STORE_PASSWORD,
System.getProperty(SSL_TRUSTSTORE_PASSWORD_JAVA_PROPERTY));
this.configuration = new HashMap<>();
// Append the read-only settings to the DDF System Settings so that all
// settings are pushed to registered listeners
configuration.putAll(readOnlySettings);
LOGGER.debug("EXITING: ctor");
}
public void setProtocol(String protocol) {
configurationProperties.put(PROTOCOL, protocol);
LOGGER.debug("protocol set to {}", protocol);
}
public void setHost(String host) {
configurationProperties.put(HOST, host);
LOGGER.debug("host set to {}", host);
}
public void setPort(String port) {
configurationProperties.put(PORT, port);
LOGGER.debug("port set to {}", port);
}
public void setId(String id) {
configurationProperties.put(SITE_NAME, id);
LOGGER.debug("site name set to {}", id);
}
public void setVersion(String version) {
configurationProperties.put(VERSION, version);
LOGGER.debug("version set to {}", version);
}
public void setOrganization(String organization) {
configurationProperties.put(ORGANIZATION, organization);
LOGGER.debug("organization set to {}", organization);
}
public void setContact(String contact) {
configurationProperties.put(CONTACT, contact);
LOGGER.debug("contact set to {}", contact);
}
/**
* Called once after all managed property setters have been called.
*/
public void init() {
updated(Collections.unmodifiableMap(configurationProperties));
}
/**
* Invoked when the DDF system settings are changed in the Admin Console,
* this method then pushes those DDF system settings to each of the
* registered ConfigurationWatchers.
*
* @param updatedConfig map of DDF system settings, not including the read-only
* settings. Can be null.
*/
public void updated(Map<String, ?> updatedConfig) {
String methodName = "updated";
LOGGER.debug("ENTERING: {}", methodName);
if (updatedConfig != null && !updatedConfig.isEmpty()) {
configuration.clear();
for (Map.Entry<String, ?> entry : updatedConfig.entrySet()) {
if (entry.getValue() != null) {
configuration.put(entry.getKey(),
entry.getValue()
.toString());
}
}
// Add the read-only settings to list to be pushed out to watchers
configuration.putAll(readOnlySettings);
}
Map<String, String> readOnlyConfig = Collections.unmodifiableMap(this.configuration);
for (ConfigurationWatcher service : services) {
service.configurationUpdateCallback(readOnlyConfig);
}
LOGGER.debug("EXITING: {}", methodName);
}
/**
* Invoked when a ConfigurationWatcher first comes online, e.g., when a
* federated source is configured, this method pushes the DDF system
* settings to the newly registered (bound) ConfigurationWatcher.
*
* @param service
* @param properties does nothing
*/
public void bind(ConfigurationWatcher service, @SuppressWarnings("rawtypes") Map properties) {
String methodName = "bind";
LOGGER.debug("ENTERING: {}", methodName);
if (service != null) {
service.configurationUpdateCallback(Collections.unmodifiableMap(this.configuration));
}
LOGGER.debug("EXITING: {}", methodName);
}
/**
* @return OSGi Configuratrion Admin service handle
*/
public ConfigurationAdmin getConfigurationAdmin() {
return configurationAdmin;
}
/**
* @param configurationAdmin
*/
public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
this.configurationAdmin = configurationAdmin;
}
/**
* Retrieves the value of an OSGi bundle's configuration property
*
* @param servicePid PID for an OSGi bundle
* @param propertyName name of the bundle's configuration property to get a value for
* @return the value of the specified bundle's configuration property
*/
public String getConfigurationValue(String servicePid, String propertyName) {
String methodName = "getConfigurationValue";
LOGGER.debug("ENTERING: {}, servicePid = {}, propertyName = {}",
methodName,
servicePid,
propertyName);
String value = "";
try {
if (this.configurationAdmin != null) {
Configuration currentConfiguration = this.configurationAdmin.getConfiguration(
servicePid);
if (currentConfiguration != null) {
Dictionary<String, Object> properties = currentConfiguration.getProperties();
if (properties != null && properties.get(propertyName) != null) {
value = (String) properties.get(propertyName);
} else {
LOGGER.debug("properties for servicePid = {} were NULL or empty",
servicePid);
}
} else {
LOGGER.debug("configuration for servicePid = {} was NULL", servicePid);
}
} else {
LOGGER.debug("configurationAdmin is NULL");
}
} catch (IOException e) {
LOGGER.info("Exception while getting configuration value.", e);
}
LOGGER.debug("EXITING: {} value = [{}]", methodName, value);
return value;
}
private Map<String, String> getSystemProperties() {
Map<String, String> map = new HashMap<>();
map.put(HTTP_PORT, SystemBaseUrl.getHttpPort());
map.put(HOST, SystemBaseUrl.getHost());
map.put(PROTOCOL, SystemBaseUrl.getProtocol());
map.put(PORT, SystemBaseUrl.getPort());
map.put(SITE_NAME, SystemInfo.getSiteName());
map.put(VERSION, SystemInfo.getVersion());
map.put(ORGANIZATION, SystemInfo.getOrganization());
map.put(SERVICES_CONTEXT_ROOT, SystemBaseUrl.getRootContext());
return map;
}
}