Set up a simple container using Fortress
You need a container for your application. This container will host the components you developed for your application.
To set up a simple container, you'll do the following tasks:
- Make a container configuration, and
- Implement a main routine
We will finally discuss how to implement a custom container class, as you may also sometimes want to.
Make the configuration
A container configuration typically contains three kinds of declarations:
- The components to use for what role
- The kind of component handling for each component
- The configuration to use for each component
Those declarations are specified in two different files. The Avalon configuration files are all XML files (we often refer to them as the xfiles) :
the <code><foo>.roles</code> file defines the components to use for what role and how the container should handle those components
the <code><foo>.xconf</code> file defines the configuration of each of the components specified in the role file
where <code><foo></code> is the name of your application or the name of one of its possible configuration.
Additionaly you must specify the logging mechanisms used by your application. This is the prupose of the <code><foo>.xlog</code> file.
The roles file
Let us see how to specify a single component :
<foo>
{{{ <role name="com.example.foo.MyComponent">
<component shorthand="myComponent" class="com.example.foo.impl.MyComponentImpl"
handler="org.apache.avalon.fortress.impl.handler.ThreadSafeComponentHandler"/>
</role> </foo>
- }}}
In the above, we do tell the container the following:
There is a component role that is <code>com.example.foo.MyComponent</code>
One of the implementation components for that role is <code>com.example.foo.impl.MyComponentImpl</code>
we will later refer to this component implementation as <code>myComponent</code> (cf below "The configuration file")
This component implementation must be handled by the container with the ThreadSafeComponentHandler (i.e. one instance for all threads)
Note that the root element name (here <code>foo</code>) is arbitrarily choosen.
For more informations, you should refer to the [Fortress/RolesFileSyntax|<code>.roles</code> file syntax reference].
The configuration file
For each of your components, you must specify a configuration node in a XML element that
has the name of the shorthand you specified in the roles file for this component (here <code>myComponent</code>)
has an <code>id</code> attribute whose value is the same shorthand
has an <code>logger</code> attribute whose value is the name of the logger category that will be used for this component (cf. below "The logger definition file")
<foo>
{{{ <myComponent id="myComponent" logger="bar">
<!-- ... here you place the configuration for the myComponent component -->
</myComponent> </foo>
- }}}
Note that this component configuration is the simplest form you may use. For more informations about configuring your components, you should refer to the [Fortress/XConfFileSyntax|<code>.xconf</code> file syntax reference].
The logger definition file
Here is a simple logger definition file. Note that this file follows the syntax of the LogKit component configuration.
<logkit>
{{{ <factories>
<factory type="file" class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/>
</factories>
<targets>
<file id="root">
<filename>../../logs/pudding.log</filename>
<format type="extended">
%7.7{priority} %23.23{time:yyyy-MM-dd HH:mm:ss:SSS} [%{category}]: %{message}\n%{throwable}
</format>
</file>
</targets>
<categories>
<category name='''''' log-level="DEBUG">
<log-target id-ref="root"/>
</category>
</categories> </logkit>
- }}}
This files simply defines
- a file target factory
a file target named <code>root</code> whose file location is <code>../../logs/pudding.log</code> and with an extender appender format.
a category named with the empty string (i.e. the root category) with a log level <code>DEBUG</code> which appends to the root target.
Refer to the LogKit component configuration for more informations.
Implement the main routine
In the main routine, you will
Set some core properties (home and work directories, configuration location...) using FortressConfig
Instanciate a DefaultContainerManager
- Initialize it
- Retrieve the container
- Do your main stuff, and finally
- Dispose the container manager
For instance the following snippet defines a class that uses a ContainerManager to create a Container. It is meant to be used by a non-avalonized piece of code, for example a main program or a servlet.
public class MyBootstrap
{
{{{ private ContainerManager m_containerManager;
public ContainerManager createContainerManager(String base) throws Exception
{
// Create the fortress context configuration object
FortressConfig config = new FortressConfig();
config.setContextDirectory( "./" );
config.setWorkDirectory( "./" );
// Specifies the configuration files location
config.setContainerConfiguration( base + "/myContainer.xconf" );
config.setLoggerManagerConfiguration( base + "/myContainer.xlog" );
config.setRoleManagerConfiguration( base + "/myContainer.roles" );
m_containerManager = new DefaultContainerManager( config.getContext() );
ContainerUtil.initialize( m_containerManager );
return m_containerManager;
}
public void disposeContainerManager()
{
ContainerUtil.dispose( m_containerManager );
}
public DefaultContainer getContainer()
{
return (DefaultContainer) m_containerManager.getContainer();
} }
- }}}
Note that we do only provide, for now, a context and a working directory along with the location of the configuration files to the ContainerManager.
Possible informations to be supplied to the FortressConfig object are:
- a custom container class
- a context directory
- a working directory
- concerning the configuration of the container
- an configuration location, or
- an configuration
- concerning the assembly of components
- an assembly configuration location, or
- an assembly configuration
- and for the processing of component management events
- the number of threads per CPU
- the thread timeout
- a command queue
- concerning the logger management
- the logger category for the container, and
- a logger manager configuration location, or
- a logger configuration, or directly
- a logger manager
- concerning the role management of the container
- a role manager configuration location, or
- a role manager configuration, or directly
- a role manager
- concerning the service management of the container
- a custom service manager, or
- a custom parent service manager
- concerning the instrumentation of the container
- an instrument manager configuration location, or
- an instrument manager configuration, or directly
- an instrument manager
You may adjust this configuration according your needs, see also AvalonFortressFAQ/ExtendingTheFortressConfiguration as example for a servlet environment.
Implement a custom container class
Let us see how to implement a custom container class. While this is not mandatory, this is sometime usefull... (DISCUSSION??)
The simplest container class you can do is a subclass of the Fortress DefaultContainer class.
public class MyContainer extends DefaultContainer
{
}For which, you have added the following line to the bootstrap code:
config.setContainerClass(MyContainer.class);
Then you can add you own component methods in order to make the container a facade for the components it contains.
public class MyContainer extends DefaultContainer
{
{{{ public Object myMethod1(Object argument) throws MyException
{
try
{
MyComponent myComponent =
(MyComponent) getServiceManager().lookup( MyComponent.ROLE );
return myComponent.doMyMethod1(argument);
}
catch (ServiceException se)
{
throw new MyException("Can't do this", se);
}
finally
{
getServiceManager().release( myComponent );
}
}
// Additional such methods... }
- }}}
You might notice that this class has nothing really specific to the fact it is a container (except its superclass). Instead, from a user point of view, we do consider that it is a component. Once you understood this, you could [Fortress/WriteSimpleContainerContainer|write a container of containers].