/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.configuration.internal; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import org.osgi.framework.BundleContext; import de.rcenvironment.core.configuration.ConfigurationException; import de.rcenvironment.core.configuration.ConfigurationSegment; import de.rcenvironment.core.configuration.ConfigurationService; import de.rcenvironment.core.configuration.ConfigurationServiceMessage; import de.rcenvironment.core.configuration.ConfigurationServiceMessageEvent; import de.rcenvironment.core.configuration.ConfigurationServiceMessageEventListener; import de.rcenvironment.core.configuration.WritableConfigurationSegment; import de.rcenvironment.core.configuration.bootstrap.BootstrapConfiguration; import de.rcenvironment.core.configuration.discovery.bootstrap.DiscoveryBootstrapService; import de.rcenvironment.core.configuration.discovery.bootstrap.DiscoveryConfiguration; import de.rcenvironment.core.utils.common.JsonUtils; import de.rcenvironment.core.utils.common.OSFamily; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.TempFileServiceAccess; import de.rcenvironment.core.utils.common.VersionUtils; /** * Implementation of the {@link ConfigurationService} using JSON as file format. * * @author Heinrich Wendel * @author Tobias Menden * @author Robert Mischke * @author Christian Weiss * @author Doreen Seider */ public class ConfigurationServiceImpl implements ConfigurationService { protected static final String CONFIGURATION_SUBDIRECTORY_PATH = "configuration"; protected static final String INTEGRATION_SUBDIRECTORY_PATH = "integration"; protected static final String RELATIVE_PATH_TO_STORAGE_ROOT = "storage"; protected static final String RELATIVE_PATH_TO_INTERNAL_DATA_ROOT = "internal"; protected static final String RELATIVE_PATH_TO_OUTPUT_ROOT = "output"; protected static final boolean PROPERTY_SUBSTITUTION_MECHANISM_ENABLED = false; protected static final String MAIN_CONFIGURATION_FILENAME = "configuration.json"; protected static final String JDBC_SUBDIRECTORY_PATH = "extras/database_connectors"; // debug option that writes/exports the active configuration to the profile's output folder private static final boolean AUTO_EXPORT_CONFIGURATION_ON_STARTUP = false; private static final String SPACE_CHARACTER = " "; private File configurationLocation; private String configurationLocationPath; private File parentTempDirectoryRoot; private final Map<ConfigurablePathId, File> configurablePathMap = new HashMap<>(); private final Map<ConfigurablePathListId, List<File>> configurablePathListMap = new HashMap<>(); private final ObjectMapper mapper; // reusable JSON mapper object /** * The merged map of key-value replacements; the namespace qualifier is merged into the map keys by the format * "<namespace>:<plain key value>". */ private Map<String, String> substitutionProperties = new HashMap<>(); // injected listeners private final List<ConfigurationServiceMessageEventListener> errorListeners = new LinkedList<>(); // injected service (if used) private DiscoveryBootstrapService discoveryBootstrapService; private final Log log = LogFactory.getLog(getClass()); private List<File> readableConfigurationDirs; private File profileDirectory; private boolean isUsingIntendedProfileDirectory; private BootstrapConfiguration bootstrapSettings; private ConfigurationStoreImpl configurationStore; private File configurationStoreFile; private ConfigurationSegment currentRootConfiguration; private GeneralSettings generalSettings; private String resolvedInstanceName; private boolean usingDefaultConfigurationValues = false; public ConfigurationServiceImpl() { mapper = JsonUtils.getDefaultObjectMapper(); // allow comments in JSON files mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); } /** * OSGi-DS life cycle method. Note that due to global initialization calls, this should not be called from unit tests; use the mock * activator methods instead. * * @param context the bundle context */ public void activate(BundleContext context) { bootstrapSettings = BootstrapConfiguration.getInstance(); initializeProfileDirFromBootstrapSettings(); initializeConfigurablePaths(); loadRootConfiguration(false); exportConfigIfConfigured(false); initializeGeneralSettings(); // initialize parent temp directory root initializeParentTempDirectoryRoot(generalSettings.getTempDirectoryOverride()); initializeInstanceTempDirectoryRoot(); if (PROPERTY_SUBSTITUTION_MECHANISM_ENABLED) { initializePropertySubstitution(); } } private synchronized void loadRootConfiguration(boolean isReload) { if (!isReload) { configurationStoreFile = new File(getProfileDirectory(), MAIN_CONFIGURATION_FILENAME); if (!configurationStoreFile.exists()) { File sampleFileLocation = new File(getConfigurablePath(ConfigurablePathId.INSTALLATION_DATA_ROOT), "examples/configuration/configuration.json.default_configuration.sample"); log.info("No configuration file found; creating a new one at " + configurationStoreFile.getAbsolutePath()); if (sampleFileLocation.isFile()) { try { FileUtils.copyFile(sampleFileLocation, configurationStoreFile); log.info("Successfully created a default configuration file at " + configurationStoreFile.getAbsolutePath()); } catch (IOException e) { log.error("Failed to copy sample configuration file from " + sampleFileLocation.getAbsolutePath() + " to " + configurationStoreFile.getAbsolutePath()); } } else { log.error("Expected configuration sample file not found at " + sampleFileLocation.getAbsolutePath()); } } // proceed in any case; if there was an error, the file may not exist configurationStore = new ConfigurationStoreImpl(configurationStoreFile); } try { currentRootConfiguration = configurationStore.getSnapshotOfRootSegment(); } catch (IOException e) { // log without (usually irrelevant) stacktrace log.error(StringUtils.format("Failed to load configuration file %s: %s", configurationStoreFile.getAbsolutePath(), e.toString())); } if (currentRootConfiguration == null) { log.error(StringUtils.format("No configuration file found, or it could not be loaded; using default values")); currentRootConfiguration = configurationStore.createEmptyPlaceholder(); usingDefaultConfigurationValues = true; } log.debug("(Re-)loaded root configuration"); } @SuppressWarnings("unused") private void exportConfigIfConfigured(boolean isReload) { if (!isReload && AUTO_EXPORT_CONFIGURATION_ON_STARTUP) { try { configurationStore.exportToFile(currentRootConfiguration, new File(getProfileDirectory(), "output/autoExportOnStartup.json")); } catch (IOException e) { log.error(e.toString()); } } } @Override public File getProfileDirectory() { return profileDirectory; } @Override public File getProfileConfigurationFile() { // FIXME 6.0.0 this path will change; adapt return new File(getConfigurablePath(ConfigurablePathId.PROFILE_ROOT), MAIN_CONFIGURATION_FILENAME); } @Override public boolean isUsingIntendedProfileDirectory() { return isUsingIntendedProfileDirectory; } @Override public boolean isIntendedProfileDirectorySuccessfullyLocked() { return bootstrapSettings.isIntendedProfileDirectorySuccessfullyLocked(); } @Override public boolean hasIntendedProfileDirectoryValidVersion() { return bootstrapSettings.hasIntendedProfileDirectoryValidVersion(); } private void initializeProfileDirFromBootstrapSettings() { profileDirectory = bootstrapSettings.getProfileDirectory(); isUsingIntendedProfileDirectory = bootstrapSettings.isIntendedProfileDirectorySuccessfullyLocked() && bootstrapSettings.hasIntendedProfileDirectoryValidVersion(); // initializeInstanceDataDirectory(); } /** * Unit test initialization method. */ protected void mockActivate() { this.bootstrapSettings = BootstrapConfiguration.getInstance(); initializeProfileDirFromBootstrapSettings(); initializeConfigurablePaths(); loadRootConfiguration(false); initializeGeneralSettings(); } @Override public void addSubstitutionProperties(String namespace, Map<String, String> properties) { if (namespace == null || namespace.isEmpty()) { throw new IllegalArgumentException("Namespace must not be null"); } // copy all entries with the namespace and a colon as an added key prefix for (Map.Entry<String, String> entry : properties.entrySet()) { substitutionProperties.put(namespace + ":" + entry.getKey(), entry.getValue()); } } @Override @Deprecated public <T> T getConfiguration(String identifier, Class<T> clazz) { log.warn("Using a legacy method to load configuration data; id = " + identifier); T configObject = null; String errorMessage = null; Throwable exception = null; File configFile; String fileName = identifier + ".json"; configFile = locateConfigurationFile(fileName); if (configFile != null) { log.debug("Loading configuration file " + configFile); try { configObject = parseConfigurationFile(clazz, configFile); } catch (JsonParseException e) { errorMessage = Messages.bind(Messages.parsingError, configFile); exception = e; } catch (JsonMappingException e) { errorMessage = Messages.bind(Messages.mappingError, configFile); exception = e; } catch (IOException e) { errorMessage = Messages.bind(Messages.couldNotOpen, configFile); exception = e; } } else { log.debug("No " + fileName + " found in any of the configuration directories " + readableConfigurationDirs + "; using default values"); } // broadcast error if parsing failed if (errorMessage != null) { log.warn(errorMessage, exception); final ConfigurationServiceMessage error = new ConfigurationServiceMessage(errorMessage); fireErrorEvent(error); } // create a new configuration instance if parsing did not succeed if (configObject == null) { try { configObject = clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { log.error("Error creating configuration object", e); throw new RuntimeException(e); } } return configObject; } @Override public ConfigurationSegment getConfigurationSegment(String relativePath) { return currentRootConfiguration.getSubSegment(relativePath); } @Override public WritableConfigurationSegment getOrCreateWritableConfigurationSegment(String path) throws ConfigurationException { return currentRootConfiguration.getOrCreateWritableSubSegment(path); } @Override public void writeConfigurationChanges() throws ConfigurationException, IOException { configurationStore.update(currentRootConfiguration); } @Override public void reloadConfiguration() { loadRootConfiguration(true); } protected <T> T parseConfigurationFile(Class<T> clazz, File configFile) throws IOException, JsonParseException, JsonMappingException { T configObject; if (configFile.exists()) { String fileContent = FileUtils.readFileToString(configFile); if (PROPERTY_SUBSTITUTION_MECHANISM_ENABLED) { fileContent = performSubstitutions(fileContent, configFile); } if (fileContent.equals("")) { return null; } try { configObject = mapper.readValue(fileContent, clazz); } catch (JsonParseException e) { String errorMessage = Messages.bind(Messages.parsingError, configFile.getAbsolutePath()); final ConfigurationServiceMessage error = new ConfigurationServiceMessage(errorMessage); fireErrorEvent(error); configObject = null; } return configObject; } else { return null; } } @Override public String resolveBundleConfigurationPath(String identifier, String path) { final String absolutePath; // TODO 5.0 review: should this accept absolute paths at all? what is the use case? - // misc_ro if (new File(path).isAbsolute()) { absolutePath = path; } else { absolutePath = configurationLocationPath + File.separator + identifier + File.separator + path; } return absolutePath; } @Override public File getConfigurablePath(ConfigurablePathId pathId) { File path = configurablePathMap.get(pathId); if (path == null) { throw new IllegalStateException("Internal error: Unconfigured path requested, id = " + pathId); } return path; } @Override public File[] getConfigurablePathList(ConfigurablePathListId pathListId) { List<File> pathList = configurablePathListMap.get(pathListId); if (pathList == null) { throw new IllegalStateException("Internal error: Unconfigured path list requested, id = " + pathListId); } return pathList.toArray(new File[pathList.size()]); } @Override public File initializeSubDirInConfigurablePath(ConfigurablePathId pathId, String relativePath) { File subDir = new File(getConfigurablePath(pathId), relativePath); subDir.mkdirs(); if (!subDir.isDirectory()) { throw new RuntimeException(new IOException("Failed to create configuration sub-directory " + subDir.getAbsolutePath())); } else { return subDir; } } @Override public File getOriginalProfileDirectory() { return bootstrapSettings.getOriginalProfileDirectory(); } @Override public String getInstanceName() { return resolvedInstanceName; } @Override public boolean getIsWorkflowHost() { return generalSettings.getIsWorkflowHost(); } @Override public boolean getIsRelay() { return generalSettings.getIsRelay(); } @Override public File getParentTempDirectoryRoot() { return parentTempDirectoryRoot; } /** * Adds a {@link ConfigurationServiceMessageEventListener} to this {@link ConfigurationService}. * * @param listener the listener to add */ public void addErrorListener(ConfigurationServiceMessageEventListener listener) { if (listener == null) { throw new NullPointerException(); } errorListeners.add(listener); log.debug(StringUtils.format("Added instance of type '%s' to the configuration service error listeners.", listener.getClass())); } /** * Removes the given {@link ConfigurationServiceMessageEventListener} from this {@link ConfigurationService}. * * @param listener the listener to remove */ public void removeErrorListener(ConfigurationServiceMessageEventListener listener) { if (listener == null) { throw new NullPointerException(); } errorListeners.remove(listener); log.debug(StringUtils.format("Removed instance of type '%s' to the configuration service error listeners.", listener.getClass())); } protected void fireErrorEvent(final ConfigurationServiceMessage error) { final ConfigurationServiceMessageEvent event = new ConfigurationServiceMessageEvent(this, error); RuntimeException exception = null; for (final ConfigurationServiceMessageEventListener listener : errorListeners) { try { listener.handleConfigurationServiceError(event); } catch (RuntimeException e) { // only cache first exception if (exception == null) { exception = e; } } } // re-throw first exception if (exception != null) { throw exception; } } protected void bindDiscoveryBootstrapService(DiscoveryBootstrapService newService) { this.discoveryBootstrapService = newService; } @Override public File getInstallationDir() { String osgiInstallArea = System.getProperty(SYSTEM_PROPERTY_OSGI_INSTALL_AREA); if (osgiInstallArea != null) { configurationLocationPath = osgiInstallArea.replace("file:", ""); configurationLocation = new File(configurationLocationPath); if (configurationLocation.isDirectory()) { // success return configurationLocation.getAbsoluteFile(); } else { throw new IllegalStateException("Property '" + SYSTEM_PROPERTY_OSGI_INSTALL_AREA + "' is defined but does not point to a directory"); } } else { throw new IllegalStateException("Property '" + SYSTEM_PROPERTY_OSGI_INSTALL_AREA + "' is null when it is required to determine the installation data directory"); } } private void initializeParentTempDirectoryRoot(String parentTempDirectoryOverride) { File parentTempDir = null; if (parentTempDirectoryOverride != null && !parentTempDirectoryOverride.trim().isEmpty()) { if (parentTempDirectoryOverride.contains(SPACE_CHARACTER)) { log.warn("Failed to initialize custom temp directory. Path '" + parentTempDirectoryOverride + "' contains whitespace(s) - trying default directory"); } else { parentTempDir = new File(resolvePlaceholdersInTempDirSetting(parentTempDirectoryOverride)); parentTempDir.mkdirs(); if (!parentTempDir.isDirectory()) { log.warn("Failed to initialize custom temp directory " + parentTempDir.getAbsolutePath() + " - trying default directory"); // clear the field to use same code path as when no custom root dir was given in // the first place parentTempDir = null; } } } if (parentTempDir == null) { String relativePath; if (OSFamily.isWindows()) { relativePath = DEFAULT_PARENT_TEMP_DIRECTORY_RELATIVE_PATH_WINDOWS; } else { // Linux, and also the fallback for unrecognized OS values relativePath = DEFAULT_PARENT_TEMP_DIRECTORY_RELATIVE_PATH_LINUX; } parentTempDir = new File(getSystemTempDir(), resolvePlaceholdersInTempDirSetting(relativePath)); parentTempDir.mkdirs(); if (!parentTempDir.isDirectory()) { // TODO >6.0.0: delegate failure handling to validator? throw new RuntimeException("Failed to initialize default temp directory " + parentTempDir.getAbsolutePath()); } } log.debug("Using parent temporary file directory " + parentTempDir.getAbsolutePath()); parentTempDirectoryRoot = parentTempDir; } private String resolvePlaceholdersInTempDirSetting(String value) { return value.replace(ConfigurationService.CONFIGURATION_PLACEHOLDER_SYSTEM_USER_NAME, System.getProperty(ConfigurationServiceImpl.SYSTEM_PROPERTY_USER_NAME)); } private void initializeGeneralSettings() { ConfigurationSegment configurationSegment = getConfigurationSegment("general"); generalSettings = new GeneralSettings(configurationSegment); resolvedInstanceName = resolvePlaceholdersInInstanceName(generalSettings.getRawInstanceName()); log.debug("Resolved instance name: " + resolvedInstanceName); } private String resolvePlaceholdersInInstanceName(String instanceName) { instanceName = instanceName.replace(CONFIGURATION_PLACEHOLDER_SYSTEM_USER_NAME, System.getProperty(SYSTEM_PROPERTY_USER_NAME)); instanceName = instanceName.replace(CONFIGURATION_PLACEHOLDER_PROFILE_NAME, profileDirectory.getName()); instanceName = instanceName.replace(CONFIGURATION_PLACEHOLDER_VERSION, StringUtils.nullSafeToString(VersionUtils.getVersionOfProduct(), "<unknown>")); // only determine the host name if actually necessary if (instanceName.contains(CONFIGURATION_PLACEHOLDER_HOST_NAME)) { try { instanceName = instanceName.replace(CONFIGURATION_PLACEHOLDER_HOST_NAME, InetAddress.getLocalHost().getHostName()); } catch (UnknownHostException e) { LogFactory.getLog(getClass()).warn("Failed to determine the local host name", e); } } return instanceName; } private void initializeInstanceTempDirectoryRoot() { String instanceTempDirectoryPrefix; // TODO this uses the last part of the instance data dir for identification - sufficient? instanceTempDirectoryPrefix = profileDirectory.getName(); try { TempFileServiceAccess.setupLiveEnvironment(parentTempDirectoryRoot, instanceTempDirectoryPrefix); } catch (IOException e) { throw new IllegalStateException( "Failed to initialize the temporary file manager after the parent root directory was successfully initialized; prefix=" + instanceTempDirectoryPrefix, e); } // TODO log instance root dir? } private void initializeConfigurablePaths() { // INSTALLATION_DATA_ROOT (default: "${osgi.install.area}/..") if (!configurePathFromOverridePropertyIfSet(ConfigurablePathId.INSTALLATION_DATA_ROOT, SYSTEM_PROPERTY_INSTALLATION_DATA_ROOT_OVERRIDE)) { File installationDirectory = getInstallationDir(); log.info("Set installation data root directory to: " + installationDirectory.getAbsolutePath()); configurablePathMap.put(ConfigurablePathId.INSTALLATION_DATA_ROOT, installationDirectory); } // get final value for internal usage final File installationDataRoot = configurablePathMap.get(ConfigurablePathId.INSTALLATION_DATA_ROOT); // USER_SETTINGS_ROOT (default: <PROFILES_PARENT_DIR>/common) if (!configurePathFromOverridePropertyIfSet(ConfigurablePathId.SHARED_USER_SETTINGS_ROOT, SYSTEM_PROPERTY_USER_SETTINGS_ROOT_OVERRIDE)) { configurablePathMap.put(ConfigurablePathId.SHARED_USER_SETTINGS_ROOT, new File(System.getProperty(SYSTEM_PROPERTY_USER_HOME), ".rce/common").getAbsoluteFile()); } // PROFILE_ROOT configurablePathMap.put(ConfigurablePathId.PROFILE_ROOT, profileDirectory); // profile subdirectories // initializeRelativeProfilePath(ConfigurablePathId.PROFILE_CONFIGURATION_DATA, // CONFIGURATION_SUBDIRECTORY_PATH); initializeRelativeProfilePath(ConfigurablePathId.PROFILE_INTEGRATION_DATA, INTEGRATION_SUBDIRECTORY_PATH); initializeRelativeProfilePath(ConfigurablePathId.PROFILE_OUTPUT, RELATIVE_PATH_TO_OUTPUT_ROOT); initializeRelativeProfilePath(ConfigurablePathId.PROFILE_DATA_MANAGEMENT, RELATIVE_PATH_TO_STORAGE_ROOT); initializeRelativeProfilePath(ConfigurablePathId.PROFILE_INTERNAL_DATA, RELATIVE_PATH_TO_INTERNAL_DATA_ROOT); // definePathAlias(ConfigurablePathId.DEFAULT_WRITEABLE_CONFIGURATION_ROOT, // ConfigurablePathId.PROFILE_CONFIGURATION_DATA); definePathAlias(ConfigurablePathId.DEFAULT_WRITEABLE_INTEGRATION_ROOT, ConfigurablePathId.PROFILE_INTEGRATION_DATA); readableConfigurationDirs = new ArrayList<>(); addDirectoryIfPresent(readableConfigurationDirs, new File(installationDataRoot, CONFIGURATION_SUBDIRECTORY_PATH)); // TODO add system settings // TODO add user settings // addDirectoryIfPresent(readableConfigurationDirs, // getConfigurablePath(ConfigurablePathId.PROFILE_CONFIGURATION_DATA)); configurablePathListMap.put(ConfigurablePathListId.READABLE_CONFIGURATION_DIRS, readableConfigurationDirs); List<File> jdbcDriverDirs = new ArrayList<>(); addDirectoryIfPresent(jdbcDriverDirs, new File(installationDataRoot, JDBC_SUBDIRECTORY_PATH)); configurablePathListMap.put(ConfigurablePathListId.JDBC_DRIVER_DIRS, jdbcDriverDirs); List<File> readableIntegrationDirs = new ArrayList<>(); addDirectoryIfPresent(readableIntegrationDirs, new File(installationDataRoot, INTEGRATION_SUBDIRECTORY_PATH)); // TODO add system settings // TODO add user settings addDirectoryIfPresent(readableIntegrationDirs, getConfigurablePath(ConfigurablePathId.PROFILE_INTEGRATION_DATA)); configurablePathListMap.put(ConfigurablePathListId.READABLE_INTEGRATION_DIRS, readableIntegrationDirs); configurablePathMap .put(ConfigurablePathId.CONFIGURATION_SAMPLES_LOCATION, new File(installationDataRoot, "examples/configuration")); log.debug("Configured paths: " + configurablePathMap); log.debug("Configured path lists: " + configurablePathListMap); log.debug("Using instance output directory " + getConfigurablePath(ConfigurablePathId.PROFILE_OUTPUT)); } private void definePathAlias(ConfigurablePathId aliasKey, ConfigurablePathId existingKey) { configurablePathMap.put(aliasKey, configurablePathMap.get(existingKey)); } private void addDirectoryIfPresent(List<File> pathList, File path) { path = path.getAbsoluteFile(); if (path.isDirectory()) { pathList.add(path); } } private boolean configurePathFromOverridePropertyIfSet(ConfigurablePathId pathId, String overrideProperty) { String value = System.getProperty(overrideProperty); if (value == null) { return false; } File path = new File(value); if (!path.isAbsolute()) { log.warn(StringUtils.format("Value '%s' for path override setting '%s' will be ignored as it is not an absolute path", value, overrideProperty)); return false; } if (!path.isDirectory()) { log.warn(StringUtils.format( "Value '%s' for path override setting '%s' will be ignored as it does not point to an existing directory", value, overrideProperty)); return false; } configurablePathMap.put(pathId, path); return true; } private void initializeRelativeProfilePath(ConfigurablePathId key, String relativePath) { File subDir = new File(profileDirectory, relativePath).getAbsoluteFile(); subDir.mkdirs(); if (!subDir.isDirectory()) { // TODO realistic enough to throw a checked exception instead? throw new RuntimeException("Unexpected state: Failed to initialize profile sub-directory " + subDir.getAbsolutePath()); } configurablePathMap.put(key, subDir); } private void initializePropertySubstitution() { // TODO >6.0.0: review, then remove or rework // add "platformProperties" as substitution values with a "platform:" prefix // this.addSubstitutionProperties("platform", // instanceConfiguration.getPlatformProperties()); // load the discovery configuration (may use "platform:" substitutions internally); // bootstrapping this from the "outside" is necessary to prevent a cyclic dependency // while making sure the discovery properties are injected before other bundles activate log.debug("Initializing property substitution"); String discoveryBundleName = discoveryBootstrapService.getSymbolicBundleName(); DiscoveryConfiguration discoveryConfiguration = getConfiguration(discoveryBundleName, DiscoveryConfiguration.class); // initialize discovery; may start a local discovery server and/or query existing servers Map<String, String> discoveryProperties = discoveryBootstrapService.initializeDiscovery(discoveryConfiguration); // register the properties learned from discovery (if any) under the "discovery" namespace this.addSubstitutionProperties("discovery", discoveryProperties); } private File locateConfigurationFile(String fileName) { for (File configDir : readableConfigurationDirs) { File file = new File(configDir, fileName); if (file.isFile()) { return file; } } return null; } private String performSubstitutions(String input, File originFile) { // shortcut if no substitution is configured if (substitutionProperties.isEmpty()) { return input; } // construct pattern to detect "${namespace:key}" and extract the "namespace:key" part Pattern pattern = Pattern.compile("\\$\\{(\\w+:\\w+)\\}"); // note: the Matcher class enforces use of StringBuffer (instead of StringBuilder) StringBuffer buffer = new StringBuffer(input.length()); // perform substitution Matcher m = pattern.matcher(input); while (m.find()) { String key = m.group(1); String value = substitutionProperties.get(key); if (value == null) { throw new IllegalArgumentException("Missing configuration value for \"" + key + "\" in file " + originFile.getAbsolutePath()); } m.appendReplacement(buffer, value); } m.appendTail(buffer); return buffer.toString(); } private String getSystemTempDir() { return System.getProperty("java.io.tmpdir"); } @Override public boolean isUsingDefaultConfigurationValues() { return usingDefaultConfigurationValues; } @Override public double[] getLocationCoordinates() { return generalSettings.getLocation(); } @Override public String getLocationName() { return generalSettings.getLocationName(); } @Override public String getInstanceContact() { return generalSettings.getContact(); } @Override public String getInstanceAdditionalInformation() { return generalSettings.getAdditionalInformation(); } }