/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.configuration2.builder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.commons.configuration2.ConfigurationUtils;
import org.apache.commons.configuration2.ImmutableConfiguration;
import org.apache.commons.configuration2.event.EventSource;
A class that allows the creation of configuration objects wrapping a ConfigurationBuilder
.
Using this class special ImmutableConfiguration
proxies can be created that delegate all method invocations to another ImmutableConfiguration
obtained from a ConfigurationBuilder
. For instance, if there is a configuration c
wrapping the builder builder
, the call c.getString(myKey)
is transformed to builder.getConfiguration().getString(myKey)
.
There are multiple use cases for such a constellation. One example is that client code can continue working with ImmutableConfiguration
objects while under the hood builders are used. Another example is that dynamic configurations can be realized in a transparent way: a client holds a single configuration (proxy) object, but the underlying builder may return a different data object on each call.
Since: 2.0
/**
* <p>
* A class that allows the creation of configuration objects wrapping a
* {@link ConfigurationBuilder}.
* </p>
* <p>
* Using this class special {@code ImmutableConfiguration} proxies can be created that
* delegate all method invocations to another {@code ImmutableConfiguration} obtained
* from a {@code ConfigurationBuilder}. For instance, if there is a
* configuration {@code c} wrapping the builder {@code builder}, the call
* {@code c.getString(myKey)} is transformed to
* {@code builder.getConfiguration().getString(myKey)}.
* </p>
* <p>
* There are multiple use cases for such a constellation. One example is that
* client code can continue working with {@code ImmutableConfiguration} objects while
* under the hood builders are used. Another example is that dynamic
* configurations can be realized in a transparent way: a client holds a single
* configuration (proxy) object, but the underlying builder may return a
* different data object on each call.
* </p>
*
* @since 2.0
*/
public class BuilderConfigurationWrapperFactory
{
The current EventSourceSupport
value. /** The current {@code EventSourceSupport} value. */
private final EventSourceSupport eventSourceSupport;
Creates a new instance of BuilderConfigurationWrapperFactory
and sets the property for supporting the EventSource
interface. Params: - evSrcSupport – the level of
EventSource
support
/**
* Creates a new instance of {@code BuilderConfigurationWrapperFactory} and
* sets the property for supporting the {@code EventSource} interface.
*
* @param evSrcSupport the level of {@code EventSource} support
*/
public BuilderConfigurationWrapperFactory(final EventSourceSupport evSrcSupport)
{
eventSourceSupport = evSrcSupport;
}
Creates a new instance of BuilderConfigurationWrapperFactory
setting the default EventSourceSupport
NONE.
/**
* Creates a new instance of {@code BuilderConfigurationWrapperFactory}
* setting the default {@code EventSourceSupport} <em>NONE</em>.
*/
public BuilderConfigurationWrapperFactory()
{
this(EventSourceSupport.NONE);
}
Creates a wrapper ImmutableConfiguration
on top of the specified ConfigurationBuilder
. This implementation delegates to createBuilderConfigurationWrapper(Class<ImmutableConfiguration>, ConfigurationBuilder<? extends ImmutableConfiguration>, EventSourceSupport)
. Params: - ifcClass – the class of the configuration objects returned by this
method; this must be an interface class and must not be
null
- builder – the wrapped
ConfigurationBuilder
(must not be null)
Type parameters: - <T> – the type of the configuration objects returned by this method
Throws: - IllegalArgumentException – if a required parameter is missing
- ConfigurationRuntimeException – if an error occurs when creating the result
ImmutableConfiguration
Returns: the wrapper configuration
/**
* Creates a wrapper {@code ImmutableConfiguration} on top of the specified
* {@code ConfigurationBuilder}. This implementation delegates to
* {@link #createBuilderConfigurationWrapper(Class, ConfigurationBuilder, EventSourceSupport)}
* .
*
* @param <T> the type of the configuration objects returned by this method
* @param ifcClass the class of the configuration objects returned by this
* method; this must be an interface class and must not be
* <b>null</b>
* @param builder the wrapped {@code ConfigurationBuilder} (must not be
* <b>null</b>)
* @return the wrapper configuration
* @throws IllegalArgumentException if a required parameter is missing
* @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error
* occurs when creating the result {@code ImmutableConfiguration}
*/
public <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper(
final Class<T> ifcClass, final ConfigurationBuilder<? extends T> builder)
{
return createBuilderConfigurationWrapper(ifcClass, builder,
getEventSourceSupport());
}
Returns the level of EventSource
support used when generating ImmutableConfiguration
objects. Returns: the level of EventSource
support
/**
* Returns the level of {@code EventSource} support used when generating
* {@code ImmutableConfiguration} objects.
*
* @return the level of {@code EventSource} support
*/
public EventSourceSupport getEventSourceSupport()
{
return eventSourceSupport;
}
Returns a ImmutableConfiguration
object which wraps the specified ConfigurationBuilder
. Each access of the configuration is delegated to a corresponding call on the ImmutableConfiguration
object managed by the builder. This is a convenience method which allows creating wrapper configurations without having to instantiate this class. Params: - ifcClass – the class of the configuration objects returned by this
method; this must be an interface class and must not be
null
- builder – the wrapped
ConfigurationBuilder
(must not be null) - evSrcSupport – the level of
EventSource
support
Type parameters: - <T> – the type of the configuration objects returned by this method
Throws: - IllegalArgumentException – if a required parameter is missing
- ConfigurationRuntimeException – if an error occurs when creating the result
ImmutableConfiguration
Returns: the wrapper configuration
/**
* Returns a {@code ImmutableConfiguration} object which wraps the specified
* {@code ConfigurationBuilder}. Each access of the configuration is
* delegated to a corresponding call on the {@code ImmutableConfiguration} object
* managed by the builder. This is a convenience method which allows
* creating wrapper configurations without having to instantiate this class.
*
* @param <T> the type of the configuration objects returned by this method
* @param ifcClass the class of the configuration objects returned by this
* method; this must be an interface class and must not be
* <b>null</b>
* @param builder the wrapped {@code ConfigurationBuilder} (must not be
* <b>null</b>)
* @param evSrcSupport the level of {@code EventSource} support
* @return the wrapper configuration
* @throws IllegalArgumentException if a required parameter is missing
* @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error
* occurs when creating the result {@code ImmutableConfiguration}
*/
public static <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper(
final Class<T> ifcClass, final ConfigurationBuilder<? extends T> builder,
final EventSourceSupport evSrcSupport)
{
if (ifcClass == null)
{
throw new IllegalArgumentException(
"Interface class must not be null!");
}
if (builder == null)
{
throw new IllegalArgumentException("Builder must not be null!");
}
return ifcClass.cast(Proxy.newProxyInstance(
BuilderConfigurationWrapperFactory.class.getClassLoader(),
fetchSupportedInterfaces(ifcClass, evSrcSupport),
new BuilderConfigurationWrapperInvocationHandler(builder,
evSrcSupport)));
}
Returns an array with the classes the generated proxy has to support.
Params: - ifcClass – the class of the configuration objects returned by this
method; this must be an interface class and must not be
null
- evSrcSupport – the level of
EventSource
support
Returns: an array with the interface classes to implement
/**
* Returns an array with the classes the generated proxy has to support.
*
* @param ifcClass the class of the configuration objects returned by this
* method; this must be an interface class and must not be
* <b>null</b>
* @param evSrcSupport the level of {@code EventSource} support
* @return an array with the interface classes to implement
*/
private static Class<?>[] fetchSupportedInterfaces(final Class<?> ifcClass,
final EventSourceSupport evSrcSupport)
{
if (EventSourceSupport.NONE == evSrcSupport)
{
return new Class<?>[] {
ifcClass
};
}
final Class<?>[] result = new Class<?>[2];
result[0] = EventSource.class;
result[1] = ifcClass;
return result;
}
An enumeration class with different options for supporting the EventSource
interface in generated ImmutableConfiguration
proxies.
Using literals of this class it is possible to specify that a ImmutableConfiguration
object returned by BuilderConfigurationWrapperFactory
also implements the EventSource
interface and how this implementation should work. See the documentation of the single constants for more details.
/**
* <p>
* An enumeration class with different options for supporting the
* {@code EventSource} interface in generated {@code ImmutableConfiguration} proxies.
* </p>
* <p>
* Using literals of this class it is possible to specify that a
* {@code ImmutableConfiguration} object returned by
* {@code BuilderConfigurationWrapperFactory} also implements the
* {@code EventSource} interface and how this implementation should work.
* See the documentation of the single constants for more details.
* </p>
*/
public enum EventSourceSupport
{
No support of the EventSource
interface. If this option is set, ImmutableConfiguration
objects generated by BuilderConfigurationWrapperFactory
do not implement the EventSource
interface. /**
* No support of the {@code EventSource} interface. If this option is
* set, {@code ImmutableConfiguration} objects generated by
* {@code BuilderConfigurationWrapperFactory} do not implement the
* {@code EventSource} interface.
*/
NONE,
Dummy support of the EventSource
interface. This option causes ImmutableConfiguration
objects generated by BuilderConfigurationWrapperFactory
to implement the EventSource
interface, however, this implementation consists only of empty dummy methods without real functionality. /**
* Dummy support of the {@code EventSource} interface. This option
* causes {@code ImmutableConfiguration} objects generated by
* {@code BuilderConfigurationWrapperFactory} to implement the
* {@code EventSource} interface, however, this implementation consists
* only of empty dummy methods without real functionality.
*/
DUMMY,
EventSource
support is implemented by delegating to the associated ConfigurationBuilder
object. If this option is used, generated ImmutableConfiguration
objects provide a fully functional implementation of EventSource
by delegating to the builder. Because the ConfigurationBuilder
interface extends EventSource
this delegation is always possible. /**
* {@code EventSource} support is implemented by delegating to the
* associated {@code ConfigurationBuilder} object. If this option is
* used, generated {@code ImmutableConfiguration} objects provide a fully
* functional implementation of {@code EventSource} by delegating to the
* builder. Because the {@code ConfigurationBuilder} interface extends
* {@code EventSource} this delegation is always possible.
*/
BUILDER
}
A specialized InvocationHandler
implementation for wrapper configurations. Here the logic of accessing a wrapped builder is implemented. /**
* A specialized {@code InvocationHandler} implementation for wrapper
* configurations. Here the logic of accessing a wrapped builder is
* implemented.
*/
private static class BuilderConfigurationWrapperInvocationHandler implements
InvocationHandler
{
The wrapped builder. /** The wrapped builder. */
private final ConfigurationBuilder<? extends ImmutableConfiguration> builder;
The level of EventSource
support. /** The level of {@code EventSource} support. */
private final EventSourceSupport eventSourceSupport;
Creates a new instance of BuilderConfigurationWrapperInvocationHandler
. Params: - wrappedBuilder – the wrapped builder
- evSrcSupport – the level of
EventSource
support
/**
* Creates a new instance of
* {@code BuilderConfigurationWrapperInvocationHandler}.
*
* @param wrappedBuilder the wrapped builder
* @param evSrcSupport the level of {@code EventSource} support
*/
public BuilderConfigurationWrapperInvocationHandler(
final ConfigurationBuilder<? extends ImmutableConfiguration> wrappedBuilder,
final EventSourceSupport evSrcSupport)
{
builder = wrappedBuilder;
eventSourceSupport = evSrcSupport;
}
Handles method invocations. This implementation handles methods of
two different interfaces:
- Methods from the
EventSource
interface are handled according to the current support level.
- Other method calls are delegated to the builder's configuration
object.
Params: - proxy – the proxy object
- method – the method to be invoked
- args – method arguments
Throws: - Throwable – if an error occurs
Returns: the return value of the method
/**
* Handles method invocations. This implementation handles methods of
* two different interfaces:
* <ul>
* <li>Methods from the {@code EventSource} interface are handled
* according to the current support level.</li>
* <li>Other method calls are delegated to the builder's configuration
* object.</li>
* </ul>
*
* @param proxy the proxy object
* @param method the method to be invoked
* @param args method arguments
* @return the return value of the method
* @throws Throwable if an error occurs
*/
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable
{
if (EventSource.class.equals(method.getDeclaringClass()))
{
return handleEventSourceInvocation(method, args);
}
return handleConfigurationInvocation(method, args);
}
Handles a method invocation on the associated builder's configuration
object.
Params: - method – the method to be invoked
- args – method arguments
Throws: - Exception – if an error occurs
Returns: the return value of the method
/**
* Handles a method invocation on the associated builder's configuration
* object.
*
* @param method the method to be invoked
* @param args method arguments
* @return the return value of the method
* @throws Exception if an error occurs
*/
private Object handleConfigurationInvocation(final Method method,
final Object[] args) throws Exception
{
return method.invoke(builder.getConfiguration(), args);
}
Handles a method invocation on the EventSource
interface. This method evaluates the current EventSourceSupport
object in order to find the appropriate target for the invocation. Params: - method – the method to be invoked
- args – method arguments
Throws: - Exception – if an error occurs
Returns: the return value of the method
/**
* Handles a method invocation on the {@code EventSource} interface.
* This method evaluates the current {@code EventSourceSupport} object
* in order to find the appropriate target for the invocation.
*
* @param method the method to be invoked
* @param args method arguments
* @return the return value of the method
* @throws Exception if an error occurs
*/
private Object handleEventSourceInvocation(final Method method, final Object[] args)
throws Exception
{
final Object target =
EventSourceSupport.DUMMY == eventSourceSupport ? ConfigurationUtils
.asEventSource(this, true) : builder;
return method.invoke(target, args);
}
}
}