    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 *     https://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 integrating
    037 * {@link MultiFileConfigurationBuilder} with {@code CombinedConfigurationBuilder}.
    038 * </p>
    039 * <p>
    040 * When using a configuration source managed by {@code MultiFileConfigurationBuilder} it is not sufficient to store the
    041 * configuration once obtained from the builder in the resulting combined configuration. Rather, it has to be ensured
    042 * that each access to this configuration queries the builder anew so that it can evaluate its file pattern and return a
    043 * different configuration if necessary. Therefore, this class returns a specialized wrapper over a
    044 * {@code MultiFileConfigurationBuilder} which returns a configuration wrapping the builder; so accessing the
    045 * configuration's properties actually calls back to the builder. This constellation is compatible with the way
    046 * {@code DynamicCombinedConfiguration} manages its data.
    047 * </p>
    048 *
    049 * @since 2.0
    050 */
    051public class MultiFileConfigurationBuilderProvider extends BaseConfigurationBuilderProvider {
    052
    053    /**
    054     * A wrapper builder implementation which also provides a {@code ReloadingController}. This class assumes that the
    055     * wrapped builder implements {@code ReloadingControllerSupport}. So the reloading controller can be obtained from this
    056     * object.
    057     */
    058    private static final class ReloadableWrapperBuilder extends WrapperBuilder implements ReloadingControllerSupport {
    059
    060        /** The object for obtaining the reloading controller. */
    061        private final ReloadingControllerSupport ctrlSupport;
    062
    063        /**
    064         * Creates a new instance of {@code ReloadableWrapperBuilder}.
    065         *
    066         * @param conf the managed configuration
    067         * @param bldr the underlying builder (must implement {@code ReloadingControllerSupport})
    068         */
    069        public ReloadableWrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) {
    070            super(conf, bldr);
    071            ctrlSupport = (ReloadingControllerSupport) bldr;
    072        }
    073
    074        @Override
    075        public ReloadingController getReloadingController() {
    076            return ctrlSupport.getReloadingController();
    077        }
    078    }
    079
    080    /**
    081     * A simple wrapper implementation of the {@code ConfigurationBuilder} interface which returns a fix configuration and
    082     * delegates to another builder for event listener management.
    083     */
    084    private static class WrapperBuilder implements ConfigurationBuilder<Configuration> {
    085
    086        /** The configuration managed by this builder. */
    087        private final Configuration configuration;
    088
    089        /** The builder to which this instance delegates. */
    090        private final ConfigurationBuilder<? extends Configuration> builder;
    091
    092        /**
    093         * Creates a new instance of {@code WrapperBuilder}.
    094         *
    095         * @param conf the managed configuration
    096         * @param bldr the underlying builder
    097         */
    098        public WrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) {
    099            configuration = conf;
    100            builder = bldr;
    101        }
    102
    103        @Override
    104        public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
    105            builder.addEventListener(eventType, listener);
    106        }
    107
    108        @Override
    109        public Configuration getConfiguration() throws ConfigurationException {
    110            return configuration;
    111        }
    112
    113        @Override
    114        public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
    115            return builder.removeEventListener(eventType, listener);
    116        }
    117    }
    118
    119    /** Constant for the name of the builder class. */
    120    private static final String BUILDER_CLASS = "org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder";
    121
    122    /** Constant for the name of the reloading builder class. */
    123    private static final String RELOADING_BUILDER_CLASS = "org.apache.commons.configuration2.builder.combined.ReloadingMultiFileConfigurationBuilder";
    124
    125    /** Constant for the name of the parameters class. */
    126    private static final String PARAM_CLASS = "org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl";
    127
    128    /**
    129     * Creates the {@code ConfigurationBuilder} to be returned by this provider. This is a very simple implementation which
    130     * always returns the same wrapper configuration instance. The handling of builder listeners is delegated to the wrapped
    131     * {@code MultiFileConfigurationBuilder}. If 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(final ConfigurationBuilder<? extends Configuration> multiBuilder,
    139        final Configuration wrapConfig) {
    140        if (multiBuilder instanceof ReloadingControllerSupport) {
    141            return new ReloadableWrapperBuilder(wrapConfig, multiBuilder);
    142        }
    143        return new WrapperBuilder(wrapConfig, multiBuilder);
    144    }
    145
    146    /**
    147     * Creates a new instance of {@code MultiFileConfigurationBuilderProvider} and sets the name of the configuration class
    148     * to be returned by {@code MultiFileConfigurationBuilder}.
    149     *
    150     * @param configCls the name of the managed configuration class
    151     * @param paramCls the name of the class of the parameters object to configure the managed configuration
    152     */
    153    public MultiFileConfigurationBuilderProvider(final String configCls, final String paramCls) {
    154        super(BUILDER_CLASS, RELOADING_BUILDER_CLASS, configCls, Arrays.asList(paramCls, PARAM_CLASS));
    155    }
    156
    157    /**
    158     * Creates a configuration which wraps the specified builder.
    159     *
    160     * @param builder the builder
    161     * @return the wrapping configuration
    162     */
    163    // It is safe to disable any type checks because we manually determine
    164    // the interface class to be passed to BuilderConfigurationWrapperFactory
    165    @SuppressWarnings({"unchecked", "rawtypes"})
    166    private Configuration createWrapperConfiguration(final ConfigurationBuilder builder) {
    167        final Class<?> configClass = ConfigurationUtils.loadClassNoEx(getConfigurationClass());
    168        final Class ifcClass = HierarchicalConfiguration.class.isAssignableFrom(configClass) ? HierarchicalConfiguration.class : Configuration.class;
    169        return (Configuration) BuilderConfigurationWrapperFactory.createBuilderConfigurationWrapper(ifcClass, builder, EventSourceSupport.BUILDER);
    170    }
    171
    172    /**
    173     * {@inheritDoc} This implementation lets the super class create a fully configured builder. Then it returns a special
    174     * wrapper around it.
    175     */
    176    @Override
    177    public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder(final ConfigurationDeclaration decl) throws ConfigurationException {
    178        final ConfigurationBuilder<? extends Configuration> multiBuilder = super.getConfigurationBuilder(decl);
    179        final Configuration wrapConfig = createWrapperConfiguration(multiBuilder);
    180        return createWrapperBuilder(multiBuilder, wrapConfig);
    181    }
    182}