ConfigurationManagerImpl.java revision 73db7653fd150e027270e0e9cc9f421ea3ce6681
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013 ForgeRock AS. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*/
/**
* A NAME does ...
*
* @author Laszlo Hordos
*/
public class ConfigurationManagerImpl
implements
/**
* Setup logging for the {@link ConfigurationManagerImpl}.
*/
private final BundleContext bundleContext;
// Three phase storage of DelayedConfigs
/**
* Store DelayedConfig before MetaDataProvider processes it.
*/
this.bundleContext = context;
ConfigurationAdmin.class, new ConfigurationAdminCustomizer());
CryptoService.class, new CryptoServiceCustomizer());
MetaDataProvider.class, this);
this);
}
void start() {
// Start monitoring Configurations and Artifact and cache then in the
// ConfigurationManager. The manager can install these configurations
// when it's ready.
// If we don't want to save configurations back to JSON files then
// disable the ConfigurationListener service from registration
if (shouldSaveConfig()) {
clazzes =
ConfigurationListener.class.getName() };
} else {
}
}
void stop() {
if (null != jsonFileInstaller) {
try {
} catch (Exception e) {
}
}
if (null != jsonFileInstallerService) {
}
if (null != selfServiceRegistration) {
// Ready to stop
}
}
// ----- Handle the lifecycle of the MetaDataProviders
if (logger.isTraceEnabled()) {
}
// If tracker process the initial bundles then the event is null so we
// check the BundleContext to make sure it's started
try {
if (providerClazzName == null) {
"No MetaDataProvider class declared in meta data file {} for {}",
} else {
try {
} catch (NoSuchMethodException e) {
// Do nothing try the default constructor
} /* catch (SecurityException e) {} */
return new MetaDataProviderHelper(
}
}
}
.getSymbolicName(), ex);
}
}
return null;
}
}
// Release resources
}
}
}
// TODO Should we fire a refresh event?
}
}
// TODO unget the service or the tracker does it?
}
// ----- Implementation of ConfigurationManager interface
throws ResourceException {
synchronized (this) {
try {
// Remove the previous versions from the delayed caches
try {
boolean wait = false;
// Process the config with the providers
.values()) {
try {
if (null != targetConfig) {
// The provider could process the config
break;
}
} catch (WaitForMetaData e) {
// TODO should we stop and delay the config or
// continue
// the processing
wait = true;
}
}
if (targetConfig == null) {
try {
if (helper instanceof MetaDataProviderHelper) {
if (null != targetConfig) {
// The provider could process the
// config
break;
}
}
} catch (WaitForMetaData e) {
// TODO should we stop and delay the config
// or
// continue the processing
wait = true;
}
}
}
}
}
} catch (NotConfiguration e) {
return null;
}
// Encrypt, Install the config or put it into the cache
if (null != targetConfig) {
if (null != configurationAdmin) {
// May throw InternalServerErrorException then
// config is rejected
} else {
}
} else {
if (null != cryptoService) {
// May throw InternalServerErrorException
if (null != configurationAdmin) {
// May throw InternalServerErrorException
// then
// config is rejected
} else {
}
} else {
}
}
} else {
}
} catch (ResourceException e) {
throw e;
} catch (ExecutionException e) {
throw new InternalServerErrorException(e);
}
}
return result;
}
}
}
} else {
try {
.getInstanceAlias());
if (null != configuration) {
}
} catch (IOException e) {
throw new InternalServerErrorException("Failed to get the configuration: "
+ e.getMessage(), e);
}
}
}
return resource;
}
throw new NotFoundException();
}
}
throws ResourceException {
boolean modified = false;
// Remove the previous versions from the delayed caches
try {
if (null != configuration) {
modified = true;
}
} catch (IOException e) {
throw new InternalServerErrorException("Failed to get the configuration: "
+ e.getMessage(), e);
}
}
if (modified) {
}
throw new NotFoundException();
}
throws ResourceException {
if (true) {
} else {
}
}
if (true) {
} else {
}
}
if (true) {
} else {
}
}
// Local reference to ConfigurationAdmin
try {
if (true) {
content));
} else {
}
}
} catch (Exception e) {
}
}
}
// ----- Non ThreadSafe methods called from CompletionService
/**
* Update the DelayedConfig in the input cache if the {@code helper} can get
* the {@code PropertiesToEncrypt} and moves these configs to the buildNext
* encryption cache.
*
* @param helper
*/
try {
if (logger.isTraceEnabled()) {
}
// Handle this config
} else {
}
}
} catch (WaitForMetaData ex) {
// provider is not yet ready to handle this configuration
} catch (NotConfiguration e) {
break;
}
}
}
/**
* Update the DelayedConfig in the encryption cache and moves these configs
* to the buildNext install cache.
*
* @param service
* @throws InternalServerErrorException
*/
}
}
}
}
throws InternalServerErrorException {
}
}
}
// ----- END Non ThreadSafe methods called from CompletionService
// ----- Util Methods
private ConfigurationAdmin getConfigurationAdmin() {
return configurationAdminTracker.getService();
}
return null;
}
private CryptoService getCryptoService() {
return cryptoServiceTracker.getService();
}
return null;
}
throws InternalServerErrorException {
if (logger.isTraceEnabled()) {
}
// Encrypt and replace value
try {
} catch (JsonCryptoException ex) {
throw new InternalServerErrorException(
"Failure during encryption of configuration "
}
}
}
}
try {
} catch (Exception e) {
throw new InternalServerErrorException("Failed to get the configuration "
}
if (existingConfig != null) {
}
try {
.getInstanceAlias());
}
}
if (logger.isDebugEnabled()) {
}
return configuration;
} catch (Exception e) {
throw new InternalServerErrorException(
"Failure to update formatted and encrypted configuration: "
+ e.getMessage(), e);
}
}
.getInstanceAlias());
if (oldConfiguration != null) {
return oldConfiguration;
} else {
/*
* if ("org.forgerock.openidm.router".equalsIgnoreCase(pid)) {
* throw new ConfigurationException(factoryPid,
* "router config can not be factory config"); }
*/
} else {
null);
}
return newConfiguration;
}
}
if (instanceAlias instanceof String) {
} else {
}
} else {
}
}
if (null == factoryPid) {
} else {
filter =
}
try {
return configurations[0];
}
} catch (InvalidSyntaxException e) {
}
return null;
}
/**
* Configure to process all JSON configuration files (if enabled)
*
* @param configAdmin
* the OSGi configuration admin service
* @throws java.io.IOException
*/
throws IOException {
// Setup the config directory
// Get the other configurations from IdentityServer
}
}
// Apply the latest configuration changes
return config;
} else {
}
return null;
}
/**
* Check each provider for meta-data for a given pid until the first match
* is found Requested each time configuration is changed so that meta data
* providers can handle additional plug-ins
*
* @param pidOrFactory
* the pid or factory pid
* @param factoryAlias
* the alias of the factory configuration newBuilder
* @return the list of properties to encrypt
*/
// public List<JsonPointer> getPropertiesToEncrypt(String pidOrFactory,
// String factoryAlias,
// JsonValue parsed) throws WaitForMetaData {
// Collection<MetaDataProvider> providers = providerTracker.getProviders();
// WaitForMetaData lastWaitException = null;
// for (MetaDataProvider provider : providers) {
// try {
// List<JsonPointer> result =
// provider.getPropertiesToEncrypt(pidOrFactory, factoryAlias, parsed);
// if (result != null) {
// return result;
// }
// } catch (WaitForMetaData ex) {
// // Continue to check if another meta data provider can resolve
// // the meta data
// lastWaitException = ex;
// }
// }
// if (lastWaitException != null) {
// throw lastWaitException;
// }
//
// return null;
// }
/**
* Encrypt properties in the configuration if necessary Also results in
* pretty print formatting of the JSON configuration.
*
* @param pidOrFactory
* the PID of either the managed service; or for factory
* configuration the PID of the Managed Service Factory
* @param instanceAlias
* null for plain managed service, or the subname (alias) for the
* managed factory configuration newBuilder
* @param config
* The OSGi configuration
* @return The configuration with any properties encrypted that a
* component's meta data marks as encrypted
* @throws org.forgerock.openidm.config.enhanced.InvalidException
* if the configuration was not valid JSON and could not be
* parsed
* @throws org.forgerock.openidm.config.InternalErrorException
* if parsing or encryption failed for technical, possibly
* transient reasons
*/
// public Dictionary encrypt(String pidOrFactory, String instanceAlias,
// Dictionary config)
// throws InvalidException, InternalErrorException, WaitForMetaData {
// // TODO Fix when cryptoService is null
// JsonValue parsed = parse(config, pidOrFactory);
// return encrypt(cryptoService, pidOrFactory, instanceAlias, config,
// parsed);
// }
//
// private Dictionary encrypt(CryptoService crypto, String pidOrFactory,
// String instanceAlias,
// Dictionary existingConfig, JsonValue newConfig) throws WaitForMetaData {
//
// JsonValue parsed = newConfig;
// Dictionary encrypted = (existingConfig == null ? new Hashtable() :
// existingConfig); // Default
// // to
// // existing
//
// List<JsonPointer> props = getPropertiesToEncrypt(pidOrFactory,
// instanceAlias, parsed);
// if (logger.isTraceEnabled()) {
// logger.trace("Properties to encrypt for {} {}: {}", new Object[] {
// pidOrFactory,
// instanceAlias, props });
// }
// if (props != null && !props.isEmpty()) {
// for (JsonPointer pointer : props) {
// logger.trace("Handling property to encrypt {}", pointer);
//
// JsonValue valueToEncrypt = parsed.get(pointer);
// if (null != valueToEncrypt && !valueToEncrypt.isNull()
// && !crypto.isEncrypted(valueToEncrypt)) {
//
// if (logger.isTraceEnabled()) {
// logger.trace("Encrypting {} with cipher {} and alias {}", new Object[] {
// pointer, ServerConstants.SECURITY_CRYPTOGRAPHY_DEFAULT_CIPHER, alias });
// }
//
// // Encrypt and replace value
// try {
// JsonValue encryptedValue =
// crypto.encrypt(valueToEncrypt,
// ServerConstants.SECURITY_CRYPTOGRAPHY_DEFAULT_CIPHER, alias);
// parsed.put(pointer, encryptedValue.getObject());
// } catch (JsonCryptoException ex) {
// throw new InternalErrorException(
// "Failure during encryption of configuration " + pidOrFactory + "-"
// + instanceAlias + " for property " + pointer.toString()
// + " : " + ex.getMessage(), ex);
// }
// }
// }
// }
// String value = null;
// try {
// ObjectWriter writer = prettyPrint.getWriter();
// value = writer.writeValueAsString(parsed.asMap());
// } catch (Exception ex) {
// throw new InternalErrorException(
// "Failure in writing formatted and encrypted configuration " +
// pidOrFactory
// + "-" + instanceAlias + " : " + ex.getMessage(), ex);
// }
//
// encrypted.put(JSONConfigInstaller.JSON_CONFIG_PROPERTY, value);
//
// if (logger.isDebugEnabled()) {
// logger.debug("Config with senstiive data encrypted {} {} : {}", new
// Object[] {
// pidOrFactory, instanceAlias, encrypted });
// }
//
// return encrypted;
// }
/**
* Parse the OSGi configuration in JSON format
*
* @param dict
* the OSGi configuration
* @param serviceName
* a name for the configuration getting parsed for logging
* purposes
* @return The parsed JSON structure
* @throws InvalidException
* if the configuration was not valid JSON and could not be
* parsed
* @throws InternalErrorException
* if parsing failed for technical, possibly transient reasons
*/
throws InvalidException {
try {
}
}
}
return jv;
}
/**
* Checks the felix.fileinstall.enableConfigSave and
* felix.fileinstall.disableConfigSave properties and determines if the
* configuration object changes should be saved in the original
* configuration files as well.
*
* @return if the changes should be saved or not
*/
private boolean shouldSaveConfig() {
}
return false;
}
return false;
}
}
return true;
}
// ----- Inner Classes
private class ConfigurationAdminCustomizer implements
try {
synchronized (ConfigurationManagerImpl.this) {
}
} catch (Exception e) {
}
try {
}
} catch (IOException e) {
}
return service;
}
// We don't need to do anything
}
if (null != jsonFileInstaller) {
try {
} catch (IOException e) {
}
}
}
}
private class CryptoServiceCustomizer implements
try {
synchronized (ConfigurationManagerImpl.this) {
if (!delayedConfigsToInstall.isEmpty()) {
if (null != configurationAdmin) {
}
}
}
} catch (Exception e) {
}
return service;
}
// We don't need to do anything
}
}
}
/**
* Represent a configuration object before it saved to
* {@link ConfigurationAdmin}
*/
private static class DelayedConfig {
final PID persistentIdentifier;
final JsonValue configuration;
this.configuration = configuration;
this.sensitiveAttributes = null;
}
/**
* Copy constructor
*/
this.sensitiveAttributes =
}
return persistentIdentifier;
}
public Resource getResource() {
}
}
.getProperties())));
}
}
}
}
/**
* Wraps a {@link MetaDataProvider} newBuilder and handles its lifecycle
* inside {@link ConfigurationManager}.
*/
static class MetaDataProviderHelper {
private final MetaDataProvider provider;
private MetaDataProviderHelper(
public void refresh() {
}
});
}
try {
synchronized (manager) {
// Update the MetaDataProviders
if (null != cryptoService) {
}
}
if (null != configurationAdmin) {
}
}
}
} catch (Exception e) {
}
// We don't care about the result
}
// ConfigurationManager has been GC-ed and this callback
// does not belong to any
}
public void destroy() {
// TODO How to release?
// provider.release();
}
/**
* Get the properties to encrypt from the wrapped
* {@code MetaDataProvider} and create a new newBuilder of
* {@link DelayedConfig} if it can handle it with the updated
* {@link DelayedConfig#sensitiveAttributes}
*
* @param config
* @return null if this {@code MetaDataProvider} can not handle the
* given {@code config}
* @throws WaitForMetaData
* when this {@code MetaDataProvider} can handle this config
* later.
*/
.getShortCanonicalName(), result);
}
}
// ----- Override
if (this == o)
return true;
return false;
// Compare the pointer
// Instead of the Object
// return provider.equals(that.provider);
}
public int hashCode() {
}
}
/**
* A wrapper around a dictionary access it as a Map
*/
static class DictionaryAsMap<U, V> extends AbstractMap<U, V> {
private Dictionary<U, V> dict;
}
return new AbstractSet<Entry<U, V>>() {
private U key;
public boolean hasNext() {
return e.hasMoreElements();
}
key = e.nextElement();
}
public void remove() {
throw new IllegalStateException();
}
}
};
}
public int size() {
}
};
}
}
private final U key;
}
public U getKey() {
return key;
}
public V getValue() {
}
}
}
}
}