    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.configuration2.builder.combined;
    018
    019import java.util.Arrays;
    020
    021import org.apache.commons.configuration2.Configuration;
    022import org.apache.commons.configuration2.ConfigurationUtils;
    023import org.apache.commons.configuration2.HierarchicalConfiguration;
    024import org.apache.commons.configuration2.builder.BuilderConfigurationWrapperFactory;
    025import org.apache.commons.configuration2.builder.BuilderConfigurationWrapperFactory.EventSourceSupport;
    026import org.apache.commons.configuration2.builder.ConfigurationBuilder;
    027import org.apache.commons.configuration2.event.Event;
    028import org.apache.commons.configuration2.event.EventListener;
    029import org.apache.commons.configuration2.event.EventType;
    030import org.apache.commons.configuration2.ex.ConfigurationException;
    031import org.apache.commons.configuration2.reloading.ReloadingController;
    032import org.apache.commons.configuration2.reloading.ReloadingControllerSupport;
    033
    034/**
    035 * <p>
    036 * A specialized {@code ConfigurationBuilderProvider} implementation for
    037 * integrating {@link MultiFileConfigurationBuilder} with
    038 * {@code CombinedConfigurationBuilder}.
    039 * </p>
    040 * <p>
    041 * When using a configuration source managed by
    042 * {@code MultiFileConfigurationBuilder} it is not sufficient to store the
    043 * configuration once obtained from the builder in the resulting combined
    044 * configuration. Rather, it has to be ensured that each access to this
    045 * configuration queries the builder anew so that it can evaluate its file
    046 * pattern and return a different configuration if necessary. Therefore, this
    047 * class returns a specialized wrapper over a
    048 * {@code MultiFileConfigurationBuilder} which returns a configuration wrapping
    049 * the builder; so accessing the configuration's properties actually calls back
    050 * to the builder. This constellation is compatible with the way
    051 * {@code DynamicCombinedConfiguration} manages its data.
    052 * </p>
    053 *
    054 * @since 2.0
    055 */
    056public class MultiFileConfigurationBuilderProvider extends
    057        BaseConfigurationBuilderProvider
    058{
    059    /** Constant for the name of the builder class. */
    060    private static final String BUILDER_CLASS =
    061            "org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder";
    062
    063    /** Constant for the name of the reloading builder class. */
    064    private static final String RELOADING_BUILDER_CLASS =
    065            "org.apache.commons.configuration2.builder.combined.ReloadingMultiFileConfigurationBuilder";
    066
    067    /** Constant for the name of the parameters class. */
    068    private static final String PARAM_CLASS =
    069            "org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl";
    070
    071    /**
    072     * Creates a new instance of {@code MultiFileConfigurationBuilderProvider}
    073     * and sets the name of the configuration class to be returned by
    074     * {@code MultiFileConfigurationBuilder}.
    075     *
    076     * @param configCls the name of the managed configuration class
    077     * @param paramCls the name of the class of the parameters object to
    078     *        configure the managed configuration
    079     */
    080    public MultiFileConfigurationBuilderProvider(final String configCls,
    081            final String paramCls)
    082    {
    083        super(BUILDER_CLASS, RELOADING_BUILDER_CLASS, configCls, Arrays.asList(
    084                paramCls, PARAM_CLASS));
    085    }
    086
    087    /**
    088     * {@inheritDoc} This implementation lets the super class create a fully
    089     * configured builder. Then it returns a special wrapper around it.
    090     */
    091    @Override
    092    public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder(
    093            final ConfigurationDeclaration decl) throws ConfigurationException
    094    {
    095        final ConfigurationBuilder<? extends Configuration> multiBuilder =
    096                super.getConfigurationBuilder(decl);
    097        final Configuration wrapConfig = createWrapperConfiguration(multiBuilder);
    098        return createWrapperBuilder(multiBuilder, wrapConfig);
    099    }
    100
    101    /**
    102     * Creates a configuration which wraps the specified builder.
    103     *
    104     * @param builder the builder
    105     * @return the wrapping configuration
    106     */
    107    // It is safe to disable any type checks because we manually determine
    108    // the interface class to be passed to BuilderConfigurationWrapperFactory
    109    @SuppressWarnings({
    110            "unchecked", "rawtypes"
    111    })
    112    private Configuration createWrapperConfiguration(
    113            final ConfigurationBuilder builder)
    114    {
    115        final Class<?> configClass =
    116                ConfigurationUtils.loadClassNoEx(getConfigurationClass());
    117        final Class ifcClass =
    118                HierarchicalConfiguration.class.isAssignableFrom(configClass) ? HierarchicalConfiguration.class
    119                        : Configuration.class;
    120        return (Configuration) BuilderConfigurationWrapperFactory
    121                .createBuilderConfigurationWrapper(ifcClass, builder,
    122                        EventSourceSupport.BUILDER);
    123    }
    124
    125    /**
    126     * Creates the {@code ConfigurationBuilder} to be returned by this provider.
    127     * This is a very simple implementation which always returns the same
    128     * wrapper configuration instance. The handling of builder listeners is
    129     * delegated to the wrapped {@code MultiFileConfigurationBuilder}. If
    130     * reloading is support, the builder returned by this method also implements
    131     * the {@link ReloadingControllerSupport} interface.
    132     *
    133     * @param multiBuilder the {@code MultiFileConfigurationBuilder}
    134     * @param wrapConfig the configuration to be returned
    135     * @return the wrapper builder
    136     */
    137    private static ConfigurationBuilder<? extends Configuration> createWrapperBuilder(
    138            final ConfigurationBuilder<? extends Configuration> multiBuilder,
    139            final Configuration wrapConfig)
    140    {
    141        if (multiBuilder instanceof ReloadingControllerSupport)
    142        {
    143            return new ReloadableWrapperBuilder(wrapConfig, multiBuilder);
    144        }
    145        return new WrapperBuilder(wrapConfig, multiBuilder);
    146    }
    147
    148    /**
    149     * A simple wrapper implementation of the {@code ConfigurationBuilder}
    150     * interface which returns a fix configuration and delegates to another
    151     * builder for event listener management.
    152     */
    153    private static class WrapperBuilder implements
    154            ConfigurationBuilder<Configuration>
    155    {
    156        /** The configuration managed by this builder. */
    157        private final Configuration configuration;
    158
    159        /** The builder to which this instance delegates. */
    160        private final ConfigurationBuilder<? extends Configuration> builder;
    161
    162        /**
    163         * Creates a new instance of {@code WrapperBuilder}.
    164         *
    165         * @param conf the managed configuration
    166         * @param bldr the underlying builder
    167         */
    168        public WrapperBuilder(final Configuration conf,
    169                final ConfigurationBuilder<? extends Configuration> bldr)
    170        {
    171            configuration = conf;
    172            builder = bldr;
    173        }
    174
    175        @Override
    176        public Configuration getConfiguration() throws ConfigurationException
    177        {
    178            return configuration;
    179        }
    180
    181        @Override
    182        public <T extends Event> void addEventListener(
    183                final EventType<T> eventType, final EventListener<? super T> listener)
    184        {
    185            builder.addEventListener(eventType, listener);
    186        }
    187
    188        @Override
    189        public <T extends Event> boolean removeEventListener(
    190                final EventType<T> eventType, final EventListener<? super T> listener)
    191        {
    192            return builder.removeEventListener(eventType, listener);
    193        }
    194    }
    195
    196    /**
    197     * A wrapper builder implementation which also provides a
    198     * {@code ReloadingController}. This class assumes that the wrapped builder
    199     * implements {@code ReloadingControllerSupport}. So the reloading
    200     * controller can be obtained from this object.
    201     */
    202    private static class ReloadableWrapperBuilder extends WrapperBuilder
    203            implements ReloadingControllerSupport
    204    {
    205        /** The object for obtaining the reloading controller. */
    206        private final ReloadingControllerSupport ctrlSupport;
    207
    208        /**
    209         * Creates a new instance of {@code ReloadableWrapperBuilder}.
    210         *
    211         * @param conf the managed configuration
    212         * @param bldr the underlying builder (must implement
    213         *        {@code ReloadingControllerSupport})
    214         */
    215        public ReloadableWrapperBuilder(final Configuration conf,
    216                final ConfigurationBuilder<? extends Configuration> bldr)
    217        {
    218            super(conf, bldr);
    219            ctrlSupport = (ReloadingControllerSupport) bldr;
    220        }
    221
    222        @Override
    223        public ReloadingController getReloadingController()
    224        {
    225            return ctrlSupport.getReloadingController();
    226        }
    227    }
    228}