blob: 155201d87c4f46b07e904d959a34bf73895d1023 [file] [log] [blame]
<?xml version="1.0"?>
<!--
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.
-->
<document>
<properties>
<title>Declaring Beans Howto</title>
</properties>
<body>
<section name="Declaring and Creating Beans">
<p>
Often it is good practice to make heavy use of Java interfaces and program
an application or components against these interfaces rather than concrete
implementation classes. This makes it possible to switch to different implementations
without having to modify calling code. However, the problem remains how a
concrete implementation of an interface is obtained. Simply using the
<b>new</b> operator on a specific implementation class would somehow break
the interface concept because then the code would have an explicit reference
to a concrete implementation.
</p>
<p>
A solution to this problem is to define the concrete implementation class
that should be used in a configuration file. Client code would obtain an
object (or a bean) from the configuration and cast it to the service
interface. This way the caller would have no knowledge about which concrete
implementation is used; it would only interact with the service through
the interface. By changing the configuration file and entering a different
class name for the implementation class the behavior of the application
can be altered, e.g. to inject a test stub for the used service.
</p>
<p>
<em>Note: The concept of defining service objects in configuration files
and let them be created by a special container has grown popular these
days. Especially IoC containers like <a href="http://www.springframework.org/">Spring</a> offer wide
functionality related to this topic. Commons Configuration is not and has
no ambitions to become an IoC container. The provided functionality for
declaring and creating beans is very basic and limited compared to the
specialists. So if you are in need of enhanced features like the creation
of complete networks of service objects, life cycle handling and such things,
you should in any case use a real IoC container. For simple use cases
however the functionality of Commons Configuration might be sufficient,
and we have tried to provide hooks for extending the predefined mechanisms.</em>
</p>
<subsection name="Basic Concepts">
<p>
Beans (we use the term <em>bean</em> here to name any plain old Java
object that is defined in a configuration file and can be instantiated
by Commons Configuration) are defined in configuration files in a specific
format, a so-called <em>Bean declaration</em>. Such a declaration contains
all information needed to create an instance of this bean class, e.g.
the fully-qualified name of the class and initialization parameters. We will
explain how a bean declaration looks like in short.
</p>
<p>
On the Java side three entities are involved in the creation of a bean:
<ul>
<li>A <em>bean factory</em>: This is an object that implements the
<code><a href="../apidocs/org/apache/commons/configuration2/beanutils/BeanFactory.html">BeanFactory</a></code>
interface and knows how to create an instance of a bean class. In most
cases calling code does not directly deal with a bean factory.</li>
<li>An implementation of the
<code><a href="../apidocs/org/apache/commons/configuration2/beanutils/BeanDeclaration.html">BeanDeclaration</a></code>
interface. This object knows how the bean declaration in the configuration
file is organized and how the needed information can be extracted. So
the way the bean is declared in the configuration file must match the
expectations of this object.</li>
<li>The helper class
<code><a href="../apidocs/org/apache/commons/configuration2/beanutils/BeanHelper.html">BeanHelper</a></code>
brings all these together and performs the bean creation operation.
Usually client code will create a <code>BeanDeclaration</code> object
from a <code>Configuration</code> implementation and then pass it to
one of the <code>createBean()</code> methods of <code>BeanHelper</code>.
That's it!</li>
</ul>
For all of the interfaces mentioned above default implementations are
provided, which in many cases can be used out of the box.
</p>
</subsection>
<subsection name="An Example">
<p>
After this theory let's get into practice using an example. Consider a
GUI application that makes use of a <em>Window manager</em> to display
its windows and dialogs to the user. There is a <code>WindowManager</code>
interface containing methods for opening, displaying, hiding, and
disposing windows. Different implementations of this interface exist, e.g.
providing different look &amp; feel or special functionality. The concrete
set of methods of the interface does not matter for this example.
</p>
<p>
Now in the application's configuration it shall be specified that the
concrete implementation <code>DefaultWindowManager</code> should be
used as <code>WindowManager</code>. This is a plain Java class implementing
the <code>WindowManager</code> interface. Some fragments are shown in
the following listing:
</p>
<source><![CDATA[
package examples.windows;
public class DefaultWindowManager implements WindowManager
{
// are windows allowed to be resized?
private boolean resizable;
// do windows have a close button?
private boolean closable;
// Default size of new windows
private int defaultWidth;
private int defaultHeight;
WindowStyleDefinition styleDefinition;
// getters and setters ommitted, also the WindowManager methods
}
]]></source>
<p>
As you can see, the <code>DefaultWindowManager</code> class has some
simple properties for defining the windows. There is also a property
named <code>StyleDefinition</code> whose type is another bean (such
a style definition may contain information about themes, colors, fonts
of the window and so on). How can we now write a configuration file so
that a bean of the <code>DefaultWindowManager</code> class is declared
and initialization properties are defined? In an XML configuration file
this will look as follows:
</p>
<source><![CDATA[
<?xml version="1.0" encoding="ISO-8859-1" ?>
<config>
<gui>
<windowManager config-class="examples.windows.DefaultWindowManager"
closable="false" resizable="true" defaultWidth="400"
defaultHeight="250">
<styleDefinition config-class="examples.windows.WindowStyleDefinition"
backColor="#ffffff" foreColor="0080ff" iconName="myicon" />
</windowManager>
</gui>
</config>
]]></source>
<p>
This XML document contains a valid bean declaration starting with the
<code>windowManager</code> element and including its sub elements. Note
the following points:
<ul>
<li>The (fully-qualified) class of the bean is specified using the
<code>config-class</code> attribute. (Attributes starting with the
prefix &quot;config-&quot; are reserved; they contain special meta
data for the bean creation process.)</li>
<li>Other attributes of the <code>windowManager</code> element correspond
to properties of the <code>DefaultWindowManager</code> class. These
properties will be initialized with the values specified here.</li>
<li>For the <code>styleDefinition</code> property, which is itself a
bean, a sub element (matching the property's name) exists. The structure
of this element is analogous to the structure of the <code>windowManager</code>
element; indeed it could even have further sub elements defining
bean properties of the <code>WindowStyleDefinition</code> class.</li>
</ul>
The basic structure of a bean declaration should have become clear by
this example.
</p>
<p>
Now let's see how we can access this declaration and create an instance.
This is demonstrated in the code fragment below:
</p>
<source><![CDATA[
Parameters params = new Parameters();
FileBasedConfigurationBuilder<XMLConfiguration> builder =
new FileBasedConfigurationBuilder<XMLConfiguration>(XMLConfiguration.class)
.configure(params.xml()
.setFileName("windowconfig.xml"));
XMLConfiguration config = builder.getConfiguration();
BeanDeclaration decl = new XMLBeanDeclaration(config, "gui.windowManager");
WindowManager wm = (WindowManager) BeanHelper.INSTANCE.createBean(decl);
]]></source>
<p>
This fragment loads the configuration file using a <code>XMLConfiguration</code>
object. Then a bean declaration object is created, in this case an
instance of the <code><a href="../apidocs/org/apache/commons/configuration2/beanutils/XMLBeanDeclaration.html">XMLBeanDeclaration</a></code>
class, which can deal with bean declarations in XML documents. This
declaration is passed to the <code>createBean()</code> method of
the default instance of the
<code><a href="../apidocs/org/apache/commons/configuration2/beanutils/BeanHelper.html">BeanHelper</a></code>
class, which returns the new bean instance.
</p>
<p>
A <code>BeanHelper</code> object does the hard work behind the scenes to
create a bean instance. It determines the class of the bean to be
created and delegates to a <code>BeanFactory</code> to create an
instance. Then all initialization properties defined in the bean
declaration are evaluated and set on the newly created bean. The
<code>BeanFactory</code> to be used is determined based on the
bean helper's configuration:
<ul>
<li>A <code>BeanHelper</code> can be configured with a number of
<code>BeanFactory</code> objects that are registered under a specific
key. The <code>BeanDeclaration</code> can contain the key of the
<code>BeanFactory</code> to be used. Please refer to the
Javadocs of <code><a href="../apidocs/org/apache/commons/configuration2/beanutils/XMLBeanDeclaration.html">
XMLBeanDeclaration</a></code> for further information.</li>
<li>When a <code>BeanHelper</code> object is constructed a default
<code>BeanFactory</code> is set. This one is used if no specific
factory is referenced by the bean declaration.</li>
</ul>
If an application does not need special bean factories, it can use the
default <code>BeanHelper</code> instance which is available via the
static <code>INSTANCE</code> member field (as shown in the example
fragment above). This instance uses a
<code><a href="../apidocs/org/apache/commons/configuration2/beanutils/DefaultBeanFactory.html">
DefaultBeanFactory</a></code> object as bean factory.
Otherwise, specialized <code>BeanHelper</code>
instances can be created that are configured with other bean factories.
A <code>BeanHelper</code> object is thread-safe and can be passed
around between the components of an application.
</p>
<p>
<code>BeanHelper</code> defines some overloaded versions of the
<code>createBean()</code> method. Some allow passing in a default bean
class; then it is not necessary to define the class in the bean declaration
- an instance of this default class will be created if it is missing in
the configuration file. If the bean cannot be created for some reason
(e.g. a wrong class name was specified), a <code>ConfigurationRuntimeException</code>
will be thrown.
</p>
</subsection>
<subsection name="Constructor arguments">
<p>
In the examples so far we assumed that beans are created using their
standard constructor. This is appropriate for classes conforming to the
Java Beans specification which requires bean classes to have such a
no-argument constructor. <em>Commons Configuration</em> also supports invoking
an arbitrary constructor passing in the corresponding constructor
arguments. Let's extend the example from the previous section and assume
that the <code>WindowStyleDefinition</code> class is now an immutable
class which has to be initialized by passing parameters to its
constructor. The constructor may look as follows:
</p>
<source><![CDATA[
public class WindowStyleDefinition
{
...
public WindowStyleDefinition(String foreground, String background,
Font stdFont)
{
...
}
...
}
]]></source>
<p>
In order to create an instance of <code>WindowStyleDefinition</code>,
we have to pass the foreground and background colors and a standard
font to the constructor. The colors are simple strings while a font is
again a complex object. The following code fragment shows the complete
bean declaration for this scenario:
</p>
<source><![CDATA[
<?xml version="1.0" encoding="ISO-8859-1" ?>
<config>
<gui>
<windowManager config-class="examples.windows.DefaultWindowManager"
closable="false" resizable="true" defaultWidth="400"
defaultHeight="250">
<styleDefinition config-class="examples.windows.WindowStyleDefinition">
<config-constrarg config-value="#0080ff"/>
<config-constrarg config-value="#ffffff"/>
<config-constrarg config-class="java.awt.Font">
<config-constrarg config-value="Monospaced"/>
<config-constrarg config-value="0"/>
<config-constrarg config-value="12"/>
</config-constrarg>
</styleDefinition>
</windowManager>
</gui>
</config>
]]></source>
<p>
The <em>styleDefinition</em> property is still defined as a nested
bean declaration in the body of the <code>&lt;windowManager&gt;</code>
element. But this time there are nested
<code>&lt;config-constrarg&gt;</code> elements. Each element defines
one constructor argument. In this example there are three constructor
arguments for the foreground color, the background color, and the
standard font. The first two arguments are simple values (i.e. primitive
Java data types) and are defined using the <code>config-value</code>
attribute. The third argument, the font, is actually another nested
bean declaration. This is indicated by the presence of the
<code>config-class</code> attribute which defines the class of this
constructor argument. To create a <code>Font</code> instance, we again
have to define constructor arguments: the font name, its style, and its
size. Note that corresponding type conversions are performed
automatically; while all values in the XML configuration file are
strings, they are converted to the correct parameter type when calling
the constructor.
</p>
<p>
<em>Commons Configuration</em> uses a pretty simple algorithm to determine the
constructor to be invoked: it is mainly based on the number of
constructor arguments. This works as expected as long as there are not
multiple constructors with the same number of arguments. If this is
the case, the developer has to provide some hints indicating which
constructor to select. Consider a bean class with the following
constructors:
</p>
<source><![CDATA[
public class MyBean
{
...
public MyBean(String svalue)
{
...
}
public MyBean(int ivalue)
{
...
}
...
}
]]></source>
<p>
This bean class has two constructors expecting a single argument. A
bean declaration like the following one will not work because it is
not clear which constructor to choose:
</p>
<source><![CDATA[
<config>
<!-- This will not work as the constructor is ambiguous! -->
<bean config-class="examples.MyBean">
<config-constrarg config-value="100"/>
</bean>
</config>
]]></source>
<p>
The solution is to explicitly specify the data type (the fully-qualified
Java class) of the constructor argument. This can be done by adding
the <code>config-type</code> attribute as in the following fragment:
</p>
<source><![CDATA[
<config>
<bean config-class="examples.MyBean">
<config-constrarg config-value="100" config-type="int"/>
</bean>
</config>
]]></source>
<p>
Now it is clear that the constructor expecting an <code>int</code>
argument is desired. The <code>config-type</code> attribute can be
used for both simple constructor arguments and nested bean
declarations. It does not have to be specified for all arguments; it
is sufficient to define a minimum number of data types so that there is
no ambiguity any more. The type specified for the attribute must be the
exact same type as expected by the constructor. This is not necessarily
the same type as the value passed to this argument. (For instance,
the constructor expects an object implementing a specific interface,
while the actual argument value is an instance of a concrete
implementation class.) Also, the <code>config-type</code> attribute is
only evaluated to determine a matching constructor; it is not related
to data type conversion.
</p>
</subsection>
<subsection name="Extending the Basic Mechanism">
<p>
As was pointed out in the introduction of this chapter, support for creating
beans is focused on the basics. But there are some possibilities of hooking
in and add custom extensions. This can be done in the following ways:
<ul>
<li>By defining a custom <code>BeanDeclaration</code> implementation</li>
<li>By providing a custom <code>BeanFactory</code> implementation</li>
</ul>
</p>
<p>
A specialized bean declaration is needed when you have to deal with
configuration files that contain bean declarations in a different format
than the ones supported by the available default implementations. Then it
is the responsibility of your implementation to parse the configuration
data and extract the required information to create the bean. Basically
your <code><a href="../apidocs/org/apache/commons/configuration2/beanutils/BeanDeclaration.html">BeanDeclaration</a></code>
implementation must be able to provide the following data:
<ul>
<li>The name of the class for which an instance is to be created.</li>
<li>The name of the bean factory that is used to create the bean. Here
<b>null</b> can be returned, then a default factory is used. (See
below for more information about working with custom bean factories.)</li>
<li>An optional parameter to be passed to the bean factory. If a factory
is used that supports additional parameters, the current parameter
values are also obtained from the bean declaration.</li>
<li>A map with the properties to be set on the newly created bean.
This map's keys are names of properties, its values are the corresponding
property values. The default bean factory will process this map and
call the corresponding setter methods on the newly created bean object.</li>
<li>A map with further <code>BeanDeclaration</code> objects for
initializing properties of the new bean that are itself beans. These
bean declarations are treated exactly as the one that is currently
processed. The resulting beans will then be set as properties on the
processed bean (the names of these properties are again obtained from
the keys of the map).</li>
</ul>
</p>
<p>
While creating a custom <code>BeanDeclaration</code> implementation
allows you to adapt the format of bean declarations in configuration files,
you can manipulate the bean creation mechanism itself by creating a
specialized implementation of the
<code><a href="../apidocs/org/apache/commons/configuration2/beanutils/BeanFactory.html">BeanFactory</a></code>
interface. For this purpose the following steps are necessary:
<ol>
<li>Create a class implementing the <code>BeanFactory</code> interface.
This interface is quite simple. It defines one method for creating an
instance of a class whose <code>Class</code> object is provided, and
another method, which is called for querying a default class.</li>
<li>Register this new factory class at the <code>BeanHelper</code>
instanced used for bean creation.</li>
<li>In the bean declaration in your configuration file refer to the
factory that should be used for creating the bean (unless this factory
is used as the <code>BeanHelper</code>'s default bean factory).</li>
</ol>
</p>
<p>
We will provide an example that covers all these steps. This example
deals with a <em>singleton</em> factory, i.e. an implementation of
<code>BeanFactory</code> that returns always the same instance of a
provided bean class.
</p>
<p>
We start with the creation of the factory class. The basic idea is that
the functionality for creating and initializing beans is already provided
by the <code><a href="../apidocs/org/apache/commons/configuration2/beanutils/DefaultBeanFactory.html">DefaultBeanFactory</a></code>
class, so we extend this class. Our implementation only has to deal with
the singleton stuff: We keep a map that stores already created bean
instances and can be accessed by the name of their classes. In the
factory's <code>createBean()</code> method we check if for the passed in
class already an instance exists. If this is the case, it is directly
returned. Otherwise we call the inherited <code>createBean()</code> method
and store its result in the map. (Note that this implementation is a bit
simplistic. A real world implementation would also have to take
initialization parameters into account and use a more sophisticated
approach to deal with concurrency issues. But for the purpose of an
example it should be good enough). Here is the code:
</p>
<source><![CDATA[
public class SingletonBeanFactory extends DefaultBeanFactory
{
/** A map for the so far created instances.*/
private final Map<String, Object> beans;
public SingletonBeanFactory()
{
super();
beans = new HashMap<String, Object>();
}
// Creates the bean. Checks if already an instance exists.
public synchronized Object createBean(BeanCreationContext bcc) throws Exception
{
Object bean = beans.get(bcc.getBeanClass().getName());
if (bean != null)
{
// Yes, there is already an instance
return bean;
}
else
{
// No, create it now (done by the super class)
bean = super.createBean(bcc);
// Store it in map
beans.put(beanClass.getName(), bean);
return bean;
}
}
}
]]></source>
<p>
The main method to define is <code>createBean()</code>. It is passed
a <code><a href="../apidocs/org/apache/commons/configuration2/beanutils/BeanCreationContext.html">
BeanCreationContext</a></code> object which contains all information
required for creating a bean (e.g. via reflection).
Note the <b>synchronized</b> key word, which is necessary because the
method can be accessed by multiple threads concurrently.
</p>
<p>
Now we have to register an instance of this class at a
<code>BeanHelper</code> instance. There are multiple ways how this can
be done. For applications making use of the default
<code>BeanHelper</code> instance, the factory can be added using the
<code>registerBeanFactory()</code> method. This can happen in the
initialization phase of your application and looks as follows:
</p>
<source><![CDATA[
BeanHelper.INSTANCE.registerBeanFactory("SINGLETON", new SingletonBeanFactory());
]]></source>
<p>
To make use of the new factory a bean declaration must contain an
attribute that refers to the name under which the factory was registered.
This is demonstrated by the fragment below:
</p>
<source><![CDATA[
<config>
...
<services>
<fileService config-class="my.package.services.FileServiceImpl"
config-factory="SINGLETON"
property1="value1" property2="value2">
<!-- Here can be nested bean declarations -->
</fileService>
...
</config>
]]></source>
<p>
In this fragment the <code>fileService</code> element contains a bean
declaration for some service object. Apart from the <code>config-class</code>
attribute the important part is the <code>config-factory</code> attribute.
This attribute tells the <code>BeanHelper</code> class that it should
use a special factory when it processes this bean declaration.
</p>
<p>
Alternatively, a separate <code>BeanHelper</code> instance can be
created passing in the new factory object as its default bean factory.
In the bean declarations, it is then no longer necessary to refer to
a specific bean factory. Below is an example showing this approach:
</p>
<source><![CDATA[
BeanHelper singletonBeanHelper = new BeanHelper(new SingletonBeanFactory());
BeanDeclaration decl = ... // somehow obtain the bean declaration
Object mySingletonBean = singletonBeanHelper.createBean(decl);
]]></source>
<p>
Of course, this new <code>BeanHelper</code> instance must now be used
everywhere where singleton beans are required. So it has to be made
available globally. Because a <code>BeanHelper</code> object is
thread-safe this can be done safely. As was demonstrated by this example,
it should not be too difficult to extend the custom mechanism for
creating beans.
</p>
</subsection>
</section>
</body>
</document>