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 1706911 2015-10-05 20:01:32Z oheger $
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(String configCls,
082            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            ConfigurationDeclaration decl) throws ConfigurationException
095    {
096        ConfigurationBuilder<? extends Configuration> multiBuilder =
097                super.getConfigurationBuilder(decl);
098        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            ConfigurationBuilder builder)
115    {
116        Class<?> configClass =
117                ConfigurationUtils.loadClassNoEx(getConfigurationClass());
118        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            ConfigurationBuilder<? extends Configuration> multiBuilder,
140            Configuration wrapConfig)
141    {
142        if (multiBuilder instanceof ReloadingControllerSupport)
143        {
144            return new ReloadableWrapperBuilder(wrapConfig, multiBuilder);
145        }
146        else
147        {
148            return new WrapperBuilder(wrapConfig, multiBuilder);
149        }
150    }
151
152    /**
153     * A simple wrapper implementation of the {@code ConfigurationBuilder}
154     * interface which returns a fix configuration and delegates to another
155     * builder for event listener management.
156     */
157    private static class WrapperBuilder implements
158            ConfigurationBuilder<Configuration>
159    {
160        /** The configuration managed by this builder. */
161        private final Configuration configuration;
162
163        /** The builder to which this instance delegates. */
164        private final ConfigurationBuilder<? extends Configuration> builder;
165
166        /**
167         * Creates a new instance of {@code WrapperBuilder}.
168         *
169         * @param conf the managed configuration
170         * @param bldr the underlying builder
171         */
172        public WrapperBuilder(Configuration conf,
173                ConfigurationBuilder<? extends Configuration> bldr)
174        {
175            configuration = conf;
176            builder = bldr;
177        }
178
179        @Override
180        public Configuration getConfiguration() throws ConfigurationException
181        {
182            return configuration;
183        }
184
185        @Override
186        public <T extends Event> void addEventListener(
187                EventType<T> eventType, EventListener<? super T> listener)
188        {
189            builder.addEventListener(eventType, listener);
190        }
191
192        @Override
193        public <T extends Event> boolean removeEventListener(
194                EventType<T> eventType, EventListener<? super T> listener)
195        {
196            return builder.removeEventListener(eventType, listener);
197        }
198    }
199
200    /**
201     * A wrapper builder implementation which also provides a
202     * {@code ReloadingController}. This class assumes that the wrapped builder
203     * implements {@code ReloadingControllerSupport}. So the reloading
204     * controller can be obtained from this object.
205     */
206    private static class ReloadableWrapperBuilder extends WrapperBuilder
207            implements ReloadingControllerSupport
208    {
209        /** The object for obtaining the reloading controller. */
210        private final ReloadingControllerSupport ctrlSupport;
211
212        /**
213         * Creates a new instance of {@code ReloadableWrapperBuilder}.
214         *
215         * @param conf the managed configuration
216         * @param bldr the underlying builder (must implement
217         *        {@code ReloadingControllerSupport})
218         */
219        public ReloadableWrapperBuilder(Configuration conf,
220                ConfigurationBuilder<? extends Configuration> bldr)
221        {
222            super(conf, bldr);
223            ctrlSupport = (ReloadingControllerSupport) bldr;
224        }
225
226        @Override
227        public ReloadingController getReloadingController()
228        {
229            return ctrlSupport.getReloadingController();
230        }
231    }
232}