Multi-tenant ConfigurationsIn a multi-tenant environment a single instance of the application will run on behalf of many clients. Typically, this will require that each client has its own unique configuration. The easiest approach to enable an application to be multi-tenant is for it to not really be aware of it at all. This requires that the configuration framework takes on some of the responsibility for making the application work correctly. One approach to enable this support in a web application might be to use a Servlet Filter and then use the Log4j or SLF4J MDC to save the attributes needed to identify the client. These attributes can then be used to identify the specific client configuration to be used. The classes described below use this technique to allow configurations to transparently provide the configuration appropriate for the clients. MultiFileConfigurationBuilder
The file-based configuration builder is stored in an internal map together with the naming pattern. The next time the same pattern is accessed, it can be retrieved from the map and used directly. If the evaluation of the pattern changes, a new file-based configuration builder is created, and a new configuration file is loaded.
When used in a combined
configuration it is often acceptable for a file
matching a particular pattern to be missing. The behavior of
DynamicCombinedConfiguration
The Sample Configuration
This sample configuration illustrates how to use
<configuration> <header> <result loggerName="TestLogger" config-class="org.apache.commons.configuration2.DynamicCombinedConfiguration" keyPattern="$${sys:Id}"> <nodeCombiner config-class="org.apache.commons.configuration2.tree.MergeCombiner"/> <expressionEngine config-class="org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine"/> </result> </header> <override> <multiFile filePattern="testMultiConfiguration_$${sys:Id}.xml" config-name="clientConfig" config-optional="true" config-forceCreate="true" schemaValidation="true"> <expressionEngine config-class="org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine"/> </multiFile> <xml fileName="testMultiConfiguration_default.xml" config-name="defaultConfig" schemaValidation="true"> <expressionEngine config-class="org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine"/> </xml> </override> </configuration>
In this example configuration definition file for a
Of special importance is the declaration of the result configuration
in form of the
Note how the variables have multiple '$'. This is how variables are escaped and
is necessary because the variables will be interpolated multiple times. Each
attempt will remove the leading '$'. When there is only a single '$' in front
of the '{' the interpolator will then resolve the variable. The first extra '$'
is necessary because Builder Configuration Related to Multi-file Configurations
When setting up a
Below is a code fragment demonstrating the set up of a
Parameters params = new Parameters(); MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder(XMLConfiguration.class) .configure(params.multiFile() .setFilePattern("configuration_${sys:Id}.xml") .setManagedBuilderParameters(params.xml() .setValidating(true) ) ); XMLConfiguration config = builder.getConfiguration(); PatternSubtreeConfigurationWrapper
Applications are often composed of many components each of which need their
own configuration. This can be accommodated by having a configuration file
per component, but this can make things hard to manage when there are many
clients and many components. A second approach is to combine them into
a single configuration file. However, this either means the subcomponent
has to be aware of the surrounding configuration and navigate past it or the
application must be provided just the portion of the configuration it
can process.
Normal practice when using dependency injection frameworks is to have the
attributes needed to make components work correctly injected into them.
When working with Commons Configuration this works very well. Components
simply need to have a
<bean id="configurationBuilder" class="org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder"> <constructor-arg index="0" value="org.apache.commons.configuration2.XMLConfiguration"/> <constructor-arg index="1"> <map> <entry key="config-fileBased"> <map> <entry key="fileName" value="configuration.xml"/> </map> </entry> </map> </constructor-arg> </bean> <bean id="applicationConfig" factory-bean="configurationBuilder" factory-method="getConfiguration"> </bean> <bean id="subcomponentConfig" class="org.apache.commons.configuration2.PatternSubtreeConfigurationWrapper" autowire='autodetect'> <constructor-arg index="0"> <ref bean="applicationConfig"/> </constructor-arg> <constructor-arg index="1" value="/Components/MyComponent"/> </bean> <bean id='MyComponent' class='org.test.MyComponent' autowire='autodetect'> <property name="configuration"> <ref bean="subcomponentConfig"/> </property> </bean>
This example shows a Spring
configuration defining beans to be used in an application. The bean
named "MyComponent" is a component with its own
configuration which is injected into the configuration
property. Here an instance of
It is also of interest how the global configuration of the
application is defined. It is loaded by a configuration builder
declared by the "configurationBuilder" bean. The fluent
API offered by configuration builders does not work very well here.
Therefore, the builder is initialized via a map with settings passed
to its constructor. Simple properties to be propagated to the
managed configuration instance can be declared directly as keys of
this map. The configuration of the file to be loaded is an
exceptional case: This information is stored internally as a
With the declaration of the configuration builder in place, the actual
configuration object can now be defined as bean using the builder's
|