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 * @since 2.0 055 */ 056public class MultiFileConfigurationBuilderProvider extends 057 BaseConfigurationBuilderProvider 058{ 059 /** Constant for the name of the builder class. */ 060 private static final String BUILDER_CLASS = 061 "org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder"; 062 063 /** Constant for the name of the reloading builder class. */ 064 private static final String RELOADING_BUILDER_CLASS = 065 "org.apache.commons.configuration2.builder.combined.ReloadingMultiFileConfigurationBuilder"; 066 067 /** Constant for the name of the parameters class. */ 068 private static final String PARAM_CLASS = 069 "org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl"; 070 071 /** 072 * Creates a new instance of {@code MultiFileConfigurationBuilderProvider} 073 * and sets the name of the configuration class to be returned by 074 * {@code MultiFileConfigurationBuilder}. 075 * 076 * @param configCls the name of the managed configuration class 077 * @param paramCls the name of the class of the parameters object to 078 * configure the managed configuration 079 */ 080 public MultiFileConfigurationBuilderProvider(final String configCls, 081 final String paramCls) 082 { 083 super(BUILDER_CLASS, RELOADING_BUILDER_CLASS, configCls, Arrays.asList( 084 paramCls, PARAM_CLASS)); 085 } 086 087 /** 088 * {@inheritDoc} This implementation lets the super class create a fully 089 * configured builder. Then it returns a special wrapper around it. 090 */ 091 @Override 092 public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder( 093 final ConfigurationDeclaration decl) throws ConfigurationException 094 { 095 final ConfigurationBuilder<? extends Configuration> multiBuilder = 096 super.getConfigurationBuilder(decl); 097 final Configuration wrapConfig = createWrapperConfiguration(multiBuilder); 098 return createWrapperBuilder(multiBuilder, wrapConfig); 099 } 100 101 /** 102 * Creates a configuration which wraps the specified builder. 103 * 104 * @param builder the builder 105 * @return the wrapping configuration 106 */ 107 // It is safe to disable any type checks because we manually determine 108 // the interface class to be passed to BuilderConfigurationWrapperFactory 109 @SuppressWarnings({ 110 "unchecked", "rawtypes" 111 }) 112 private Configuration createWrapperConfiguration( 113 final ConfigurationBuilder builder) 114 { 115 final Class<?> configClass = 116 ConfigurationUtils.loadClassNoEx(getConfigurationClass()); 117 final Class ifcClass = 118 HierarchicalConfiguration.class.isAssignableFrom(configClass) ? HierarchicalConfiguration.class 119 : Configuration.class; 120 return (Configuration) BuilderConfigurationWrapperFactory 121 .createBuilderConfigurationWrapper(ifcClass, builder, 122 EventSourceSupport.BUILDER); 123 } 124 125 /** 126 * Creates the {@code ConfigurationBuilder} to be returned by this provider. 127 * This is a very simple implementation which always returns the same 128 * wrapper configuration instance. The handling of builder listeners is 129 * delegated to the wrapped {@code MultiFileConfigurationBuilder}. If 130 * reloading is support, the builder returned by this method also implements 131 * the {@link ReloadingControllerSupport} interface. 132 * 133 * @param multiBuilder the {@code MultiFileConfigurationBuilder} 134 * @param wrapConfig the configuration to be returned 135 * @return the wrapper builder 136 */ 137 private static ConfigurationBuilder<? extends Configuration> createWrapperBuilder( 138 final ConfigurationBuilder<? extends Configuration> multiBuilder, 139 final Configuration wrapConfig) 140 { 141 if (multiBuilder instanceof ReloadingControllerSupport) 142 { 143 return new ReloadableWrapperBuilder(wrapConfig, multiBuilder); 144 } 145 return new WrapperBuilder(wrapConfig, multiBuilder); 146 } 147 148 /** 149 * A simple wrapper implementation of the {@code ConfigurationBuilder} 150 * interface which returns a fix configuration and delegates to another 151 * builder for event listener management. 152 */ 153 private static class WrapperBuilder implements 154 ConfigurationBuilder<Configuration> 155 { 156 /** The configuration managed by this builder. */ 157 private final Configuration configuration; 158 159 /** The builder to which this instance delegates. */ 160 private final ConfigurationBuilder<? extends Configuration> builder; 161 162 /** 163 * Creates a new instance of {@code WrapperBuilder}. 164 * 165 * @param conf the managed configuration 166 * @param bldr the underlying builder 167 */ 168 public WrapperBuilder(final Configuration conf, 169 final ConfigurationBuilder<? extends Configuration> bldr) 170 { 171 configuration = conf; 172 builder = bldr; 173 } 174 175 @Override 176 public Configuration getConfiguration() throws ConfigurationException 177 { 178 return configuration; 179 } 180 181 @Override 182 public <T extends Event> void addEventListener( 183 final EventType<T> eventType, final EventListener<? super T> listener) 184 { 185 builder.addEventListener(eventType, listener); 186 } 187 188 @Override 189 public <T extends Event> boolean removeEventListener( 190 final EventType<T> eventType, final EventListener<? super T> listener) 191 { 192 return builder.removeEventListener(eventType, listener); 193 } 194 } 195 196 /** 197 * A wrapper builder implementation which also provides a 198 * {@code ReloadingController}. This class assumes that the wrapped builder 199 * implements {@code ReloadingControllerSupport}. So the reloading 200 * controller can be obtained from this object. 201 */ 202 private static class ReloadableWrapperBuilder extends WrapperBuilder 203 implements ReloadingControllerSupport 204 { 205 /** The object for obtaining the reloading controller. */ 206 private final ReloadingControllerSupport ctrlSupport; 207 208 /** 209 * Creates a new instance of {@code ReloadableWrapperBuilder}. 210 * 211 * @param conf the managed configuration 212 * @param bldr the underlying builder (must implement 213 * {@code ReloadingControllerSupport}) 214 */ 215 public ReloadableWrapperBuilder(final Configuration conf, 216 final ConfigurationBuilder<? extends Configuration> bldr) 217 { 218 super(conf, bldr); 219 ctrlSupport = (ReloadingControllerSupport) bldr; 220 } 221 222 @Override 223 public ReloadingController getReloadingController() 224 { 225 return ctrlSupport.getReloadingController(); 226 } 227 } 228}