View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.builder.combined;
18  
19  import java.util.Arrays;
20  
21  import org.apache.commons.configuration2.Configuration;
22  import org.apache.commons.configuration2.ConfigurationUtils;
23  import org.apache.commons.configuration2.HierarchicalConfiguration;
24  import org.apache.commons.configuration2.builder.BuilderConfigurationWrapperFactory;
25  import org.apache.commons.configuration2.builder.BuilderConfigurationWrapperFactory.EventSourceSupport;
26  import org.apache.commons.configuration2.builder.ConfigurationBuilder;
27  import org.apache.commons.configuration2.event.Event;
28  import org.apache.commons.configuration2.event.EventListener;
29  import org.apache.commons.configuration2.event.EventType;
30  import org.apache.commons.configuration2.ex.ConfigurationException;
31  import org.apache.commons.configuration2.reloading.ReloadingController;
32  import org.apache.commons.configuration2.reloading.ReloadingControllerSupport;
33  
34  /**
35   * <p>
36   * A specialized {@code ConfigurationBuilderProvider} implementation for integrating
37   * {@link MultiFileConfigurationBuilder} with {@code CombinedConfigurationBuilder}.
38   * </p>
39   * <p>
40   * When using a configuration source managed by {@code MultiFileConfigurationBuilder} it is not sufficient to store the
41   * configuration once obtained from the builder in the resulting combined configuration. Rather, it has to be ensured
42   * that each access to this configuration queries the builder anew so that it can evaluate its file pattern and return a
43   * different configuration if necessary. Therefore, this class returns a specialized wrapper over a
44   * {@code MultiFileConfigurationBuilder} which returns a configuration wrapping the builder; so accessing the
45   * configuration's properties actually calls back to the builder. This constellation is compatible with the way
46   * {@code DynamicCombinedConfiguration} manages its data.
47   * </p>
48   *
49   * @since 2.0
50   */
51  public class MultiFileConfigurationBuilderProvider extends BaseConfigurationBuilderProvider {
52  
53      /**
54       * A wrapper builder implementation which also provides a {@code ReloadingController}. This class assumes that the
55       * wrapped builder implements {@code ReloadingControllerSupport}. So the reloading controller can be obtained from this
56       * object.
57       */
58      private static final class ReloadableWrapperBuilder extends WrapperBuilder implements ReloadingControllerSupport {
59  
60          /** The object for obtaining the reloading controller. */
61          private final ReloadingControllerSupport ctrlSupport;
62  
63          /**
64           * Creates a new instance of {@code ReloadableWrapperBuilder}.
65           *
66           * @param conf the managed configuration
67           * @param bldr the underlying builder (must implement {@code ReloadingControllerSupport})
68           */
69          public ReloadableWrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) {
70              super(conf, bldr);
71              ctrlSupport = (ReloadingControllerSupport) bldr;
72          }
73  
74          @Override
75          public ReloadingController getReloadingController() {
76              return ctrlSupport.getReloadingController();
77          }
78      }
79  
80      /**
81       * A simple wrapper implementation of the {@code ConfigurationBuilder} interface which returns a fix configuration and
82       * delegates to another builder for event listener management.
83       */
84      private static class WrapperBuilder implements ConfigurationBuilder<Configuration> {
85  
86          /** The configuration managed by this builder. */
87          private final Configuration configuration;
88  
89          /** The builder to which this instance delegates. */
90          private final ConfigurationBuilder<? extends Configuration> builder;
91  
92          /**
93           * Creates a new instance of {@code WrapperBuilder}.
94           *
95           * @param conf the managed configuration
96           * @param bldr the underlying builder
97           */
98          public WrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) {
99              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 }