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 * A wrapper builder implementation which also provides a {@code ReloadingController}. This class assumes that the
54 * wrapped builder implements {@code ReloadingControllerSupport}. So the reloading controller can be obtained from this
55 * object.
56 */
57 private static final class ReloadableWrapperBuilder extends WrapperBuilder implements ReloadingControllerSupport {
58 /** The object for obtaining the reloading controller. */
59 private final ReloadingControllerSupport ctrlSupport;
60
61 /**
62 * Creates a new instance of {@code ReloadableWrapperBuilder}.
63 *
64 * @param conf the managed configuration
65 * @param bldr the underlying builder (must implement {@code ReloadingControllerSupport})
66 */
67 public ReloadableWrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) {
68 super(conf, bldr);
69 ctrlSupport = (ReloadingControllerSupport) bldr;
70 }
71
72 @Override
73 public ReloadingController getReloadingController() {
74 return ctrlSupport.getReloadingController();
75 }
76 }
77
78 /**
79 * A simple wrapper implementation of the {@code ConfigurationBuilder} interface which returns a fix configuration and
80 * delegates to another builder for event listener management.
81 */
82 private static class WrapperBuilder implements ConfigurationBuilder<Configuration> {
83 /** The configuration managed by this builder. */
84 private final Configuration configuration;
85
86 /** The builder to which this instance delegates. */
87 private final ConfigurationBuilder<? extends Configuration> builder;
88
89 /**
90 * Creates a new instance of {@code WrapperBuilder}.
91 *
92 * @param conf the managed configuration
93 * @param bldr the underlying builder
94 */
95 public WrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) {
96 configuration = conf;
97 builder = bldr;
98 }
99
100 @Override
101 public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
102 builder.addEventListener(eventType, listener);
103 }
104
105 @Override
106 public Configuration getConfiguration() throws ConfigurationException {
107 return configuration;
108 }
109
110 @Override
111 public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
112 return builder.removeEventListener(eventType, listener);
113 }
114 }
115
116 /** Constant for the name of the builder class. */
117 private static final String BUILDER_CLASS = "org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder";
118
119 /** Constant for the name of the reloading builder class. */
120 private static final String RELOADING_BUILDER_CLASS = "org.apache.commons.configuration2.builder.combined.ReloadingMultiFileConfigurationBuilder";
121
122 /** Constant for the name of the parameters class. */
123 private static final String PARAM_CLASS = "org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl";
124
125 /**
126 * Creates the {@code ConfigurationBuilder} to be returned by this provider. This is a very simple implementation which
127 * always returns the same wrapper configuration instance. The handling of builder listeners is delegated to the wrapped
128 * {@code MultiFileConfigurationBuilder}. If reloading is support, the builder returned by this method also implements
129 * the {@link ReloadingControllerSupport} interface.
130 *
131 * @param multiBuilder the {@code MultiFileConfigurationBuilder}
132 * @param wrapConfig the configuration to be returned
133 * @return the wrapper builder
134 */
135 private static ConfigurationBuilder<? extends Configuration> createWrapperBuilder(final ConfigurationBuilder<? extends Configuration> multiBuilder,
136 final Configuration wrapConfig) {
137 if (multiBuilder instanceof ReloadingControllerSupport) {
138 return new ReloadableWrapperBuilder(wrapConfig, multiBuilder);
139 }
140 return new WrapperBuilder(wrapConfig, multiBuilder);
141 }
142
143 /**
144 * Creates a new instance of {@code MultiFileConfigurationBuilderProvider} and sets the name of the configuration class
145 * to be returned by {@code MultiFileConfigurationBuilder}.
146 *
147 * @param configCls the name of the managed configuration class
148 * @param paramCls the name of the class of the parameters object to configure the managed configuration
149 */
150 public MultiFileConfigurationBuilderProvider(final String configCls, final String paramCls) {
151 super(BUILDER_CLASS, RELOADING_BUILDER_CLASS, configCls, Arrays.asList(paramCls, PARAM_CLASS));
152 }
153
154 /**
155 * Creates a configuration which wraps the specified builder.
156 *
157 * @param builder the builder
158 * @return the wrapping configuration
159 */
160 // It is safe to disable any type checks because we manually determine
161 // the interface class to be passed to BuilderConfigurationWrapperFactory
162 @SuppressWarnings({"unchecked", "rawtypes"})
163 private Configuration createWrapperConfiguration(final ConfigurationBuilder builder) {
164 final Class<?> configClass = ConfigurationUtils.loadClassNoEx(getConfigurationClass());
165 final Class ifcClass = HierarchicalConfiguration.class.isAssignableFrom(configClass) ? HierarchicalConfiguration.class : Configuration.class;
166 return (Configuration) BuilderConfigurationWrapperFactory.createBuilderConfigurationWrapper(ifcClass, builder, EventSourceSupport.BUILDER);
167 }
168
169 /**
170 * {@inheritDoc} This implementation lets the super class create a fully configured builder. Then it returns a special
171 * wrapper around it.
172 */
173 @Override
174 public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder(final ConfigurationDeclaration decl) throws ConfigurationException {
175 final ConfigurationBuilder<? extends Configuration> multiBuilder = super.getConfigurationBuilder(decl);
176 final Configuration wrapConfig = createWrapperConfiguration(multiBuilder);
177 return createWrapperBuilder(multiBuilder, wrapConfig);
178 }
179 }