    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 * @version $Id: MultiFileConfigurationBuilderProvider.java 1842194 2018-09-27 22:24:23Z ggregory $
    055 * @since 2.0
    056 */
    057public class MultiFileConfigurationBuilderProvider extends
    058        BaseConfigurationBuilderProvider
    059{
    060    /** Constant for the name of the builder class. */
    061    private static final String BUILDER_CLASS =
    062            "org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder";
    063
    064    /** Constant for the name of the reloading builder class. */
    065    private static final String RELOADING_BUILDER_CLASS =
    066            "org.apache.commons.configuration2.builder.combined.ReloadingMultiFileConfigurationBuilder";
    067
    068    /** Constant for the name of the parameters class. */
    069    private static final String PARAM_CLASS =
    070            "org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl";
    071
    072    /**
    073     * Creates a new instance of {@code MultiFileConfigurationBuilderProvider}
    074     * and sets the name of the configuration class to be returned by
    075     * {@code MultiFileConfigurationBuilder}.
    076     *
    077     * @param configCls the name of the managed configuration class
    078     * @param paramCls the name of the class of the parameters object to
    079     *        configure the managed configuration
    080     */
    081    public MultiFileConfigurationBuilderProvider(final String configCls,
    082            final String paramCls)
    083    {
    084        super(BUILDER_CLASS, RELOADING_BUILDER_CLASS, configCls, Arrays.asList(
    085                paramCls, PARAM_CLASS));
    086    }
    087
    088    /**
    089     * {@inheritDoc} This implementation lets the super class create a fully
    090     * configured builder. Then it returns a special wrapper around it.
    091     */
    092    @Override
    093    public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder(
    094            final ConfigurationDeclaration decl) throws ConfigurationException
    095    {
    096        final ConfigurationBuilder<? extends Configuration> multiBuilder =
    097                super.getConfigurationBuilder(decl);
    098        final Configuration wrapConfig = createWrapperConfiguration(multiBuilder);
    099        return createWrapperBuilder(multiBuilder, wrapConfig);
    100    }
    101
    102    /**
    103     * Creates a configuration which wraps the specified builder.
    104     *
    105     * @param builder the builder
    106     * @return the wrapping configuration
    107     */
    108    // It is safe to disable any type checks because we manually determine
    109    // the interface class to be passed to BuilderConfigurationWrapperFactory
    110    @SuppressWarnings({
    111            "unchecked", "rawtypes"
    112    })
    113    private Configuration createWrapperConfiguration(
    114            final ConfigurationBuilder builder)
    115    {
    116        final Class<?> configClass =
    117                ConfigurationUtils.loadClassNoEx(getConfigurationClass());
    118        final Class ifcClass =
    119                HierarchicalConfiguration.class.isAssignableFrom(configClass) ? HierarchicalConfiguration.class
    120                        : Configuration.class;
    121        return (Configuration) BuilderConfigurationWrapperFactory
    122                .createBuilderConfigurationWrapper(ifcClass, builder,
    123                        EventSourceSupport.BUILDER);
    124    }
    125
    126    /**
    127     * Creates the {@code ConfigurationBuilder} to be returned by this provider.
    128     * This is a very simple implementation which always returns the same
    129     * wrapper configuration instance. The handling of builder listeners is
    130     * delegated to the wrapped {@code MultiFileConfigurationBuilder}. If
    131     * reloading is support, the builder returned by this method also implements
    132     * the {@link ReloadingControllerSupport} interface.
    133     *
    134     * @param multiBuilder the {@code MultiFileConfigurationBuilder}
    135     * @param wrapConfig the configuration to be returned
    136     * @return the wrapper builder
    137     */
    138    private static ConfigurationBuilder<? extends Configuration> createWrapperBuilder(
    139            final ConfigurationBuilder<? extends Configuration> multiBuilder,
    140            final Configuration wrapConfig)
    141    {
    142        if (multiBuilder instanceof ReloadingControllerSupport)
    143        {
    144            return new ReloadableWrapperBuilder(wrapConfig, multiBuilder);
    145        }
    146        return new WrapperBuilder(wrapConfig, multiBuilder);
    147    }
    148
    149    /**
    150     * A simple wrapper implementation of the {@code ConfigurationBuilder}
    151     * interface which returns a fix configuration and delegates to another
    152     * builder for event listener management.
    153     */
    154    private static class WrapperBuilder implements
    155            ConfigurationBuilder<Configuration>
    156    {
    157        /** The configuration managed by this builder. */
    158        private final Configuration configuration;
    159
    160        /** The builder to which this instance delegates. */
    161        private final ConfigurationBuilder<? extends Configuration> builder;
    162
    163        /**
    164         * Creates a new instance of {@code WrapperBuilder}.
    165         *
    166         * @param conf the managed configuration
    167         * @param bldr the underlying builder
    168         */
    169        public WrapperBuilder(final Configuration conf,
    170                final ConfigurationBuilder<? extends Configuration> bldr)
    171        {
    172            configuration = conf;
    173            builder = bldr;
    174        }
    175
    176        @Override
    177        public Configuration getConfiguration() throws ConfigurationException
    178        {
    179            return configuration;
    180        }
    181
    182        @Override
    183        public <T extends Event> void addEventListener(
    184                final EventType<T> eventType, final EventListener<? super T> listener)
    185        {
    186            builder.addEventListener(eventType, listener);
    187        }
    188
    189        @Override
    190        public <T extends Event> boolean removeEventListener(
    191                final EventType<T> eventType, final EventListener<? super T> listener)
    192        {
    193            return builder.removeEventListener(eventType, listener);
    194        }
    195    }
    196
    197    /**
    198     * A wrapper builder implementation which also provides a
    199     * {@code ReloadingController}. This class assumes that the wrapped builder
    200     * implements {@code ReloadingControllerSupport}. So the reloading
    201     * controller can be obtained from this object.
    202     */
    203    private static class ReloadableWrapperBuilder extends WrapperBuilder
    204            implements ReloadingControllerSupport
    205    {
    206        /** The object for obtaining the reloading controller. */
    207        private final ReloadingControllerSupport ctrlSupport;
    208
    209        /**
    210         * Creates a new instance of {@code ReloadableWrapperBuilder}.
    211         *
    212         * @param conf the managed configuration
    213         * @param bldr the underlying builder (must implement
    214         *        {@code ReloadingControllerSupport})
    215         */
    216        public ReloadableWrapperBuilder(final Configuration conf,
    217                final ConfigurationBuilder<? extends Configuration> bldr)
    218        {
    219            super(conf, bldr);
    220            ctrlSupport = (ReloadingControllerSupport) bldr;
    221        }
    222
    223        @Override
    224        public ReloadingController getReloadingController()
    225        {
    226            return ctrlSupport.getReloadingController();
    227        }
    228    }
    229}