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 }