blob: 164868cb6b78db519e263109ea5e9c8556160f5a [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.commons.configuration2.builder.combined;
import java.util.Arrays;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.ConfigurationUtils;
import org.apache.commons.configuration2.HierarchicalConfiguration;
import org.apache.commons.configuration2.builder.BuilderConfigurationWrapperFactory;
import org.apache.commons.configuration2.builder.BuilderConfigurationWrapperFactory.EventSourceSupport;
import org.apache.commons.configuration2.builder.ConfigurationBuilder;
import org.apache.commons.configuration2.event.Event;
import org.apache.commons.configuration2.event.EventListener;
import org.apache.commons.configuration2.event.EventType;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.reloading.ReloadingController;
import org.apache.commons.configuration2.reloading.ReloadingControllerSupport;
/**
* <p>
* A specialized {@code ConfigurationBuilderProvider} implementation for
* integrating {@link MultiFileConfigurationBuilder} with
* {@code CombinedConfigurationBuilder}.
* </p>
* <p>
* When using a configuration source managed by
* {@code MultiFileConfigurationBuilder} it is not sufficient to store the
* configuration once obtained from the builder in the resulting combined
* configuration. Rather, it has to be ensured that each access to this
* configuration queries the builder anew so that it can evaluate its file
* pattern and return a different configuration if necessary. Therefore, this
* class returns a specialized wrapper over a
* {@code MultiFileConfigurationBuilder} which returns a configuration wrapping
* the builder; so accessing the configuration's properties actually calls back
* to the builder. This constellation is compatible with the way
* {@code DynamicCombinedConfiguration} manages its data.
* </p>
*
* @version $Id$
* @since 2.0
*/
public class MultiFileConfigurationBuilderProvider extends
BaseConfigurationBuilderProvider
{
/** Constant for the name of the builder class. */
private static final String BUILDER_CLASS =
"org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder";
/** Constant for the name of the reloading builder class. */
private static final String RELOADING_BUILDER_CLASS =
"org.apache.commons.configuration2.builder.combined.ReloadingMultiFileConfigurationBuilder";
/** Constant for the name of the parameters class. */
private static final String PARAM_CLASS =
"org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl";
/**
* Creates a new instance of {@code MultiFileConfigurationBuilderProvider}
* and sets the name of the configuration class to be returned by
* {@code MultiFileConfigurationBuilder}.
*
* @param configCls the name of the managed configuration class
* @param paramCls the name of the class of the parameters object to
* configure the managed configuration
*/
public MultiFileConfigurationBuilderProvider(String configCls,
String paramCls)
{
super(BUILDER_CLASS, RELOADING_BUILDER_CLASS, configCls, Arrays.asList(
paramCls, PARAM_CLASS));
}
/**
* {@inheritDoc} This implementation lets the super class create a fully
* configured builder. Then it returns a special wrapper around it.
*/
@Override
public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder(
ConfigurationDeclaration decl) throws ConfigurationException
{
ConfigurationBuilder<? extends Configuration> multiBuilder =
super.getConfigurationBuilder(decl);
Configuration wrapConfig = createWrapperConfiguration(multiBuilder);
return createWrapperBuilder(multiBuilder, wrapConfig);
}
/**
* Creates a configuration which wraps the specified builder.
*
* @param builder the builder
* @return the wrapping configuration
*/
// It is safe to disable any type checks because we manually determine
// the interface class to be passed to BuilderConfigurationWrapperFactory
@SuppressWarnings({
"unchecked", "rawtypes"
})
private Configuration createWrapperConfiguration(
ConfigurationBuilder builder)
{
Class<?> configClass =
ConfigurationUtils.loadClassNoEx(getConfigurationClass());
Class ifcClass =
HierarchicalConfiguration.class.isAssignableFrom(configClass) ? HierarchicalConfiguration.class
: Configuration.class;
return (Configuration) BuilderConfigurationWrapperFactory
.createBuilderConfigurationWrapper(ifcClass, builder,
EventSourceSupport.BUILDER);
}
/**
* Creates the {@code ConfigurationBuilder} to be returned by this provider.
* This is a very simple implementation which always returns the same
* wrapper configuration instance. The handling of builder listeners is
* delegated to the wrapped {@code MultiFileConfigurationBuilder}. If
* reloading is support, the builder returned by this method also implements
* the {@link ReloadingControllerSupport} interface.
*
* @param multiBuilder the {@code MultiFileConfigurationBuilder}
* @param wrapConfig the configuration to be returned
* @return the wrapper builder
*/
private static ConfigurationBuilder<? extends Configuration> createWrapperBuilder(
ConfigurationBuilder<? extends Configuration> multiBuilder,
Configuration wrapConfig)
{
if (multiBuilder instanceof ReloadingControllerSupport)
{
return new ReloadableWrapperBuilder(wrapConfig, multiBuilder);
}
else
{
return new WrapperBuilder(wrapConfig, multiBuilder);
}
}
/**
* A simple wrapper implementation of the {@code ConfigurationBuilder}
* interface which returns a fix configuration and delegates to another
* builder for event listener management.
*/
private static class WrapperBuilder implements
ConfigurationBuilder<Configuration>
{
/** The configuration managed by this builder. */
private final Configuration configuration;
/** The builder to which this instance delegates. */
private final ConfigurationBuilder<? extends Configuration> builder;
/**
* Creates a new instance of {@code WrapperBuilder}.
*
* @param conf the managed configuration
* @param bldr the underlying builder
*/
public WrapperBuilder(Configuration conf,
ConfigurationBuilder<? extends Configuration> bldr)
{
configuration = conf;
builder = bldr;
}
@Override
public Configuration getConfiguration() throws ConfigurationException
{
return configuration;
}
@Override
public <T extends Event> void addEventListener(
EventType<T> eventType, EventListener<? super T> listener)
{
builder.addEventListener(eventType, listener);
}
@Override
public <T extends Event> boolean removeEventListener(
EventType<T> eventType, EventListener<? super T> listener)
{
return builder.removeEventListener(eventType, listener);
}
}
/**
* A wrapper builder implementation which also provides a
* {@code ReloadingController}. This class assumes that the wrapped builder
* implements {@code ReloadingControllerSupport}. So the reloading
* controller can be obtained from this object.
*/
private static class ReloadableWrapperBuilder extends WrapperBuilder
implements ReloadingControllerSupport
{
/** The object for obtaining the reloading controller. */
private final ReloadingControllerSupport ctrlSupport;
/**
* Creates a new instance of {@code ReloadableWrapperBuilder}.
*
* @param conf the managed configuration
* @param bldr the underlying builder (must implement
* {@code ReloadingControllerSupport})
*/
public ReloadableWrapperBuilder(Configuration conf,
ConfigurationBuilder<? extends Configuration> bldr)
{
super(conf, bldr);
ctrlSupport = (ReloadingControllerSupport) bldr;
}
@Override
public ReloadingController getReloadingController()
{
return ctrlSupport.getReloadingController();
}
}
}