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 * https://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 integrating 037 * {@link MultiFileConfigurationBuilder} with {@code CombinedConfigurationBuilder}. 038 * </p> 039 * <p> 040 * When using a configuration source managed by {@code MultiFileConfigurationBuilder} it is not sufficient to store the 041 * configuration once obtained from the builder in the resulting combined configuration. Rather, it has to be ensured 042 * that each access to this configuration queries the builder anew so that it can evaluate its file pattern and return a 043 * different configuration if necessary. Therefore, this class returns a specialized wrapper over a 044 * {@code MultiFileConfigurationBuilder} which returns a configuration wrapping the builder; so accessing the 045 * configuration's properties actually calls back to the builder. This constellation is compatible with the way 046 * {@code DynamicCombinedConfiguration} manages its data. 047 * </p> 048 * 049 * @since 2.0 050 */ 051public class MultiFileConfigurationBuilderProvider extends BaseConfigurationBuilderProvider { 052 053 /** 054 * A wrapper builder implementation which also provides a {@code ReloadingController}. This class assumes that the 055 * wrapped builder implements {@code ReloadingControllerSupport}. So the reloading controller can be obtained from this 056 * object. 057 */ 058 private static final class ReloadableWrapperBuilder extends WrapperBuilder implements ReloadingControllerSupport { 059 060 /** The object for obtaining the reloading controller. */ 061 private final ReloadingControllerSupport ctrlSupport; 062 063 /** 064 * Creates a new instance of {@code ReloadableWrapperBuilder}. 065 * 066 * @param conf the managed configuration 067 * @param bldr the underlying builder (must implement {@code ReloadingControllerSupport}) 068 */ 069 public ReloadableWrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) { 070 super(conf, bldr); 071 ctrlSupport = (ReloadingControllerSupport) bldr; 072 } 073 074 @Override 075 public ReloadingController getReloadingController() { 076 return ctrlSupport.getReloadingController(); 077 } 078 } 079 080 /** 081 * A simple wrapper implementation of the {@code ConfigurationBuilder} interface which returns a fix configuration and 082 * delegates to another builder for event listener management. 083 */ 084 private static class WrapperBuilder implements ConfigurationBuilder<Configuration> { 085 086 /** The configuration managed by this builder. */ 087 private final Configuration configuration; 088 089 /** The builder to which this instance delegates. */ 090 private final ConfigurationBuilder<? extends Configuration> builder; 091 092 /** 093 * Creates a new instance of {@code WrapperBuilder}. 094 * 095 * @param conf the managed configuration 096 * @param bldr the underlying builder 097 */ 098 public WrapperBuilder(final Configuration conf, final ConfigurationBuilder<? extends Configuration> bldr) { 099 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}