blob: d08e3bc806770e10c0763c5b1ec1edfe79dbb1df [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.qpid.server.configuration.plugins;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.ConversionException;
import org.apache.log4j.Logger;
import org.apache.qpid.server.configuration.ConfigurationManager;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
public abstract class ConfigurationPlugin
{
protected static final Logger _logger = Logger.getLogger(ConfigurationPlugin.class);
private Map<String, ConfigurationPlugin>
_pluginConfiguration = new HashMap<String, ConfigurationPlugin>();
private Configuration _config;
/**
* The Elements that this Plugin can process.
*
* For a Queues plugin that would be a list containing:
* <ul>
* <li>queue - the queue entries
* <li>the alerting values for defaults
* <li>exchange - the default exchange
* <li>durable - set the default durablity
* </ul>
*/
abstract public String[] getElementsProcessed();
/** Performs configuration validation. */
public void validateConfiguration() throws ConfigurationException
{
// Override in sub-classes
}
public Configuration getConfig()
{
return _config;
}
public <C extends ConfigurationPlugin> C getConfiguration(String plugin)
{
return (C) _pluginConfiguration.get(plugin);
}
/**
* Sets the configuration for this plugin
*
* @param path
* @param configuration the configuration for this plugin.
*/
public void setConfiguration(String path, Configuration configuration) throws ConfigurationException
{
_config = configuration;
// Extract a list of elements for processing
Iterator<?> keys = configuration.getKeys();
Set<String> elements = new HashSet<String>();
while (keys.hasNext())
{
String key = (String) keys.next();
int elementNameIndex = key.indexOf(".");
String element = key.trim();
if (elementNameIndex != -1)
{
element = key.substring(0, elementNameIndex).trim();
}
// Trim any element properties
elementNameIndex = element.indexOf("[");
if (elementNameIndex > 0)
{
element = element.substring(0, elementNameIndex).trim();
}
elements.add(element);
}
//Remove the items we already expect in the configuration
for (String tag : getElementsProcessed())
{
// Work round the issue with Commons configuration.
// With an XMLConfiguration the key will be [@property]
// but with a CompositeConfiguration it will be @property].
// Hide this issue from our users so when/if we change the
// configuration they don't have to.
int bracketIndex = tag.indexOf("[");
if (bracketIndex != -1)
{
tag = tag.substring(bracketIndex + 1, tag.length());
}
elements.remove(tag);
}
if (_logger.isInfoEnabled())
{
if (!elements.isEmpty())
{
_logger.info("Elements to lookup:" + path);
for (String tag : elements)
{
_logger.info("Tag:'" + tag + "'");
}
}
}
offerRemainingConfigurationToOtherPlugins(path, configuration, elements);
validateConfiguration();
}
private void offerRemainingConfigurationToOtherPlugins(String path,
Configuration configuration, Set<String> elements) throws ConfigurationException
{
final IApplicationRegistry appRegistry = safeGetApplicationRegistryInstance();
if (appRegistry == null)
{
// We see this happen during shutdown due to asynchronous reconfig using IO threads.
// Need to remove the responsibility for offering configuration to other class.
_logger.info("Cannot offer remaining config to other plugins, can't find app registry");
return;
}
final ConfigurationManager configurationManager = appRegistry.getConfigurationManager();
// Process the elements in the configuration
for (String element : elements)
{
Configuration handled = element.length() == 0 ? configuration : configuration.subset(element);
String configurationElement = element;
if (path.length() > 0)
{
configurationElement = path + "." + configurationElement;
}
List<ConfigurationPlugin> handlers = configurationManager.getConfigurationPlugins(configurationElement, handled);
if(_logger.isDebugEnabled())
{
_logger.debug("For '" + element + "' found handlers (" + handlers.size() + "):" + handlers);
}
for (ConfigurationPlugin plugin : handlers)
{
_pluginConfiguration.put(plugin.getClass().getName(), plugin);
}
}
}
private IApplicationRegistry safeGetApplicationRegistryInstance()
{
try
{
return ApplicationRegistry.getInstance();
}
catch (IllegalStateException ise)
{
return null;
}
}
/** Helper method to print out list of keys in a {@link Configuration}. */
public static final void showKeys(Configuration config)
{
if (config.isEmpty())
{
_logger.info("Configuration is empty");
}
else
{
Iterator<?> keys = config.getKeys();
while (keys.hasNext())
{
String key = (String) keys.next();
_logger.info("Configuration key: " + key);
}
}
}
protected boolean hasConfiguration()
{
return _config != null;
}
/// Getters
protected double getDoubleValue(String property)
{
return getDoubleValue(property, 0.0);
}
protected double getDoubleValue(String property, double defaultValue)
{
return _config.getDouble(property, defaultValue);
}
protected long getLongValue(String property)
{
return getLongValue(property, 0);
}
protected long getLongValue(String property, long defaultValue)
{
return _config.getLong(property, defaultValue);
}
protected int getIntValue(String property)
{
return getIntValue(property, 0);
}
protected int getIntValue(String property, int defaultValue)
{
return _config.getInt(property, defaultValue);
}
protected String getStringValue(String property)
{
return getStringValue(property, null);
}
protected String getStringValue(String property, String defaultValue)
{
return _config.getString(property, defaultValue);
}
protected boolean getBooleanValue(String property)
{
return getBooleanValue(property, false);
}
protected boolean getBooleanValue(String property, boolean defaultValue)
{
return _config.getBoolean(property, defaultValue);
}
protected List getListValue(String property)
{
return getListValue(property, Collections.EMPTY_LIST);
}
protected List getListValue(String property, List defaultValue)
{
return _config.getList(property, defaultValue);
}
/// Validation Helpers
protected boolean contains(String property)
{
return _config.getProperty(property) != null;
}
/**
* Provide mechanism to validate Configuration contains a Postiive Long Value
*
* @param property
*
* @throws ConfigurationException
*/
protected void validatePositiveLong(String property) throws ConfigurationException
{
try
{
if (!containsPositiveLong(property))
{
throw new ConfigurationException(this.getClass().getSimpleName()
+ ": '" + property +
"' must be a Positive Long value.");
}
}
catch (Exception e)
{
Throwable last = e;
// Find the first cause
if (e instanceof ConversionException)
{
Throwable t = e.getCause();
while (t != null)
{
last = t;
t = last.getCause();
}
}
throw new ConfigurationException(this.getClass().getSimpleName() +
": unable to configure invalid " +
property + ":" +
_config.getString(property),
last);
}
}
protected boolean containsLong(String property)
{
try
{
_config.getLong(property);
return true;
}
catch (NoSuchElementException e)
{
return false;
}
}
protected boolean containsPositiveLong(String property)
{
try
{
long value = _config.getLong(property);
return value > 0;
}
catch (NoSuchElementException e)
{
return false;
}
}
protected boolean containsInt(String property)
{
try
{
_config.getInt(property);
return true;
}
catch (NoSuchElementException e)
{
return false;
}
}
protected boolean containsBoolean(String property)
{
try
{
_config.getBoolean(property);
return true;
}
catch (NoSuchElementException e)
{
return false;
}
}
/**
* Given another configuration merge the configuration into our own config
*
* The new values being merged in will take precedence over existing values.
*
* In the simplistic case this means something like:
*
* So if we have configuration set
* name = 'fooo'
*
* And the new configuration contains a name then that will be reset.
* name = 'new'
*
* However this plugin will simply contain other plugins so the merge will
* be called until we end up at a base plugin that understand how to merge
* items. i.e Alerting values. Where the provided configuration will take
* precedence.
*
* @param configuration the config to merge in to our own.
*/
public void addConfiguration(ConfigurationPlugin configuration)
{
// If given configuration is null then there is nothing to process.
if (configuration == null)
{
return;
}
// Merge all the sub configuration items
for (Map.Entry<String, ConfigurationPlugin> newPlugins : configuration._pluginConfiguration.entrySet())
{
String key = newPlugins.getKey();
ConfigurationPlugin config = newPlugins.getValue();
if (_pluginConfiguration.containsKey(key))
{
//Merge the configuration if we already have this type of config
_pluginConfiguration.get(key).mergeConfiguration(config);
}
else
{
//otherwise just add it to our config.
_pluginConfiguration.put(key, config);
}
}
//Merge the configuration itself
String key = configuration.getClass().getName();
if (_pluginConfiguration.containsKey(key))
{
//Merge the configuration if we already have this type of config
_pluginConfiguration.get(key).mergeConfiguration(configuration);
}
else
{
//If we are adding a configuration of our own type then merge
if (configuration.getClass() == this.getClass())
{
mergeConfiguration(configuration);
}
else
{
// just store this in case someone else needs it.
_pluginConfiguration.put(key, configuration);
}
}
}
protected void mergeConfiguration(ConfigurationPlugin configuration)
{
_config = configuration.getConfig();
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("\n").append(getClass().getSimpleName());
sb.append("=[ (").append(formatToString()).append(")");
for(Map.Entry<String,ConfigurationPlugin> item : _pluginConfiguration.entrySet())
{
sb.append("\n").append(item.getValue());
}
sb.append("]\n");
return sb.toString();
}
public String formatToString()
{
return super.toString();
}
protected void setConfig(Configuration config)
{
_config = config;
}
}