|
Jakarta main
Avalon main
About
Chapters
Printer Friendly
|
Implementing the Dream
We will show how you can use Avalon Framework and Avalon Excalibur to
realize your services. We will show just how easy Avalon is to use.
After your analysis is complete, you need to create the Components and
Services that make up your system. Avalon would be of little use if it
only described some idioms for you to use. Even then, the use of those
idioms and patterns would still help in understanding the overall system.
Avalon Excalibur provides some useful Components and utilities that you can
incorporate into your own system that will make your life much easier. For
our demonstration, we will go through the process of defining a Component
that retrieves a document instance from a repository. If you recall our
discussion about the theoretical Business Server, we identified this
Component as a Service. In practical situations, a Service is a Component
that has a larger scope.
Implementing the Component |
At this point, we define how to implement our Component. We will go
through the process of implementing the DocumentRepository Component
previously mentioned. The first things we need to figure out are the
concern areas for our Component. Then we have to figure out how our
Component will be created and managed.
Choosing the Concern Areas |
We have already defined the Role and the Interface for our
DocumentRepository Component in the last chapter, we are ready to
create the implementation. Because the interface for the
DocumentRepository only defines one method, we have an opportunity to
create a thread-safe Component. This is the most desired type of
component because it allows for the least amount of resource
utilization. In order for our implementation to be thread-safe, we do
need to be careful about how we implement the Component. Since all of
our documents are stored in a database, and we desire to use an
external Guardian Component, we will need access to other Components.
As responsible developers, we will want to log messages that will help
us debug our component, and track down what is going on internally.
The beauty of the Avalon Framework is that you only implement the
interfaces you need, and ignore the ones you don't. This is where
Separation of Concerns pays off. As you find you need a new concern
area addressed, you merely implement the associated interface, and
incorporate the new functionality. To the client of your Component,
there is no difference.
Since it is a design goal to be thread-safe, we already know that we
need to implement the ThreadSafe interface. The DocumentRepository
interface only has one method, so the use of the Component's work
interface is compatible with that requirement. Furthermore, we know
that a Component will not be used before it is fully initialized, nor
will it be used once it is destroyed.
There are a couple of implicit interfaces that we need to implement to
accomplish the design. We want our solution to be as secure as
possible and explicitly track whether the Component is fully
initialized or not. To accomplish this goal, we will implement the
Initializable and Disposable interfaces. Since specific information
about our environment may change, or may need to be customized, we need
to make our DocumentRepository Configurable. Our Component makes use
of other Components, and the method that Avalon provides to get
instances of the required Component is by using a ComponentManager. We
will need to implement the Composable interface to get an instance of
the ComponentManager.
Because the DocumentRepository accesses the documents in the database,
we need to make a decision. Do we want to take advantage of the Avalon
Excalibur DataSourceComponent, or do we want to implement our own
Connection management code. For the sake of this paper, we will use
the DataSourceComponent.
At this point, our skeleton class looks like this:
public class DatabaseDocumentRepository
extends AbstractLogEnabled
implements DocumentRepository , Configurable, Composable, Initializable,
Disposable, Component, ThreadSafe
{
private boolean initialized = false;
private boolean disposed = false;
private ComponentManager manager = null;
private String dbResource = null;
/**
* Constructor. All Components need a public no argument constructor
* to be a legal Component.
*/
public DatabaseDocumentRepository() {}
/**
* Configuration. Notice that I check to see if the Component has
* already been configured? This is done to enforce the policy of
* only calling Configure once.
*/
public final void configure(Configuration conf)
throws ConfigurationException
{
if (initialized || disposed)
{
throw new IllegalStateException ("Illegal call");
}
if (null == this.dbResource)
{
this.dbResource = conf.getChild("dbpool").getValue();
getLogger().debug("Using database pool: " + this.dbResource);
// Notice the getLogger()? This is from AbstractLogEnabled
// which I extend for just about all my components.
}
}
/**
* Composition. Notice that I check to see if the Component has
* already been initialized or disposed? This is done to enforce
* the policy of proper lifecycle management.
*/
public final void compose(ComponentManager cmanager)
throws ComponentException
{
if (initialized || disposed)
{
throw new IllegalStateException ("Illegal call");
}
if (null == this.manager)
{
this.manager = cmanager;
}
}
public final void initialize()
throws Exception
{
if (null == this.manager)
{
throw new IllegalStateException("Not Composed");
}
if (null == this.dbResource)
{
throw new IllegalStateException("Not Configured");
}
if (disposed)
{
throw new IllegalStateException("Already disposed");
}
this.initialized = true;
}
public final void dispose()
{
this.disposed = true;
this.manager = null;
this.dbResource = null;
}
public final Document getDocument(Principal requestor, int refId)
{
if (!initialized || disposed)
{
throw new IllegalStateException("Illegal call");
}
// TODO: FILL IN LOGIC
}
}
|
You will notice some constructs in the above code. When you are
designing with security in mind, you should explicitly enforce every
contract on your Component. Security is only as strong as the weakest
link. You should only use a Component when you are certain it is fully
initialized, and never use it when it is disposed of. I placed the
logic that you would need in this skeleton class because that way you
can adopt the same practices in classes that you write.
|
Instantiating and Managing Components |
In order for you to understand how the Container/Component relationship
works, we will first discuss the manual method of managing Components.
Next, we will discuss how Avalon's Excalibur Component infrastructure
hides the complexity from you. You will still find times when you
would rather manage components yourself. Most of the time the power
and flexibility of Excalibur is just what you need.
The Manual Method |
All of Avalon's Components are created somewhere. The code that
creates the Component is that Component's Container. The Container
is responsible for managing the Component's lifecycle from
construction through destruction. A Container can be the static
"main" method called from a command line, or it can be another
Component. Remember the Inversion of Control pattern when you
design your Containers. Information and method calls should only
flow from the Container to the Component.
Subversion of Control |
Subversion of Control is the anti-pattern to Inversion of Control.
Subversion of control is done when you pass a reference to a
Component's Container to the Component. It is also done when you
have a Component manage it's own lifecycle. Code that operates in
this manner should be considered defective. The interactions that
happen when you confuse the Container/Component relationship make
the system harder to debug and security harder to audit.
|
In order to manage the child Components, you need to keep a reference
to them for their entire lifetime. Before the Container or any other
Component can use the child Component, it must go through the
initialization phase of its lifecycle. For our DocumentRepository,
the code will look something like the following:
class ContainerComponent implements Component, Initializable, Disposable
{
DocumentRepository docs = new DatabaseDocumentRepository();
GuardianComponent guard = new DocumentGuardianComponent();
DefaultComponentManager manager = new DefaultComponentManager();
public void initialize()
throws Exception
{
Logger docLogger = new LogKitLogger( Hierarchy.defaultHierarchy()
.getLoggerFor( "document" ) );
this.docs.enableLogging( docLogger.childLogger( "repository" ) );
this.guard.enableLogging( docLogger.childLogger( "security" ) );
DefaultConfiguration pool = new DefaultConfiguration("dbpool");
pool.setValue("main-pool");
DefaultConfiguration conf = new DefaultConfiguration("");
conf.addChild(pool);
this.manager.addComponent( DocumentRepository.ROLE, this.docs );
this.manager.addComponent( GuardianComponent.ROLE, this.guard );
this.docs.compose( this.manager );
this.guard.compose( this.manager );
this.docs.configure(conf);
this.guard.initialize();
this.docs.initialize();
}
public void dispose()
{
this.docs.dispose();
this.guard.dispose();
}
}
|
For the sake of brevity, I removed all the explicit checking from the
above code. You can see that manually creating and managing
Components is very detailed. If you forget to do one step in the
life of a Component, you will see bugs. This also requires intimate
knowledge of the Components you are instantiating. An alternate
approach would be to add a couple methods to the above
ContainerComponent that handles the
initialization of the components dynamically.
|
Automated Autonomy |
Developer's are naturally lazy, so they would spend the time to write
a specialized ComponentManager that became the Container for all of
their Components in the system. That way they would not have to be
bothered with intimately knowing the interfaces of all the Components
in a system. That can be a daunting task. The Avalon developers
have created just such a beast. Avalon Excalibur's Component
architecture includes a ComponentManager that is controlled by
configuration files written in XML.
There is a tradeoff when you relinquish the responsibility of
managing a Component to Excalibur's ComponentManager. You relinquish
the fine control over what Components are included in the
ComponentManager. However, if you have a large system, you will find
that manual control is a daunting task. In that case, it is better
for the stability of the system for one entity to centrally manage
all the Components in a system.
Since there are varying levels of integration you want to achieve
with Excalibur's Component Architecture, we will start with the
lowest level. Excalibur has a group of ComponentHandler objects that
act as individual Containers for one type of Component. They manage
the complete life of your Component. Let me introduce the concept of
lifestyle interfaces. A lifestyle interface describes how the system
treats a Component. Since the lifestyle of a component has impact on
the running of a system, we need to discuss the implications of the
current lifestyle interfaces:
-
org.apache.avalon.framework.thread.SingleThreaded
-
Not thread-safe or reusable.
-
When no lifestyle interface is supplied, this is assumed.
-
A brand new instance is created every time the Component is
requested.
-
Creation and initialization is delayed until you request the
Component.
-
org.apache.avalon.framework.thread.Threadsafe
-
Component is fully reentrant, and complies with all
principles of thread safety.
-
One instance is created and shared with all Composables that
request it.
-
Creation and initialization is done when ComponentHandler is
created.
-
org.apache.avalon.excalibur.pool.Poolable
-
Not thread-safe, but is fully reusable.
-
A pool of instances is created and the free instances are
returned to Composables that request it.
-
Creation and initialization is done when ComponentHandler is
created.
The ComponentHandler interface is very simple to deal with. You
initialize the Constructor with the Java class, the Configuration
object, the ComponentManager, a Context object, and a RoleManager.
If you know that your Component will not need any of the
aforementioned items, you can pass a null in its place. After
that, when you need a reference to the Component, you call the "get"
method. After you are done with it, you call the "put" method and
pass the Component back to the ComponentHandler. The following code
will make it easier to understand.
class ContainerComponent implements Component, Initializable, Disposable
{
ComponentHandler docs = null;
ComponentHandler guard = null;
DefaultComponentManager manager = new DefaultComponentManager();
public void initialize()
throws Exception
{
DefaultConfiguration pool = new DefaultConfiguration("dbpool");
pool.setValue("main-pool");
DefaultConfiguration conf = new DefaultConfiguration("");
conf.addChild(pool);
this.docs.configure(conf);
this.docs = ComponentHandler.getComponentHandler(
DatabaseDocumentRepository.class,
conf, this.manager, null, null);
this.guard = ComponentHandler.getComponentHandler(
DocumentGuardianComponent.class,
null, this.manager, null, null);
Logger docLogger = new LogKitLogger( Hierarchy.defaultHierarchy()
.getLoggerFor( "document" ) );
this.docs.enableLogging( docLogger.childLogger( "repository" ) );
this.guard.enableLogging( docLogger.childLogger( "security" ) );
this.manager.addComponent(DocumentRepository.ROLE, this.docs);
this.manager.addComponent(GuardianComponent.ROLE, this.guard);
this.guard.initialize();
this.docs.initialize();
}
public void dispose()
{
this.docs.dispose();
this.guard.dispose();
}
}
|
At this point, we only saved ourselves a few lines of code. We still
manually created our Configuration object, we still had to set the
Logger, and we still had to initialize and dispose of the
ComponentHandler objects. What we did at this point is simply protect
ourselves from changing interfaces. You may find it better for your
code to use this approach. Excalibur went further though. Most
complex systems have configuration files, and they allow an
administrator to alter vital Configuration information. Excalibur
can read a configuration file in the following format, and build the
Components in a system from it.
<my-system>
<component
role="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
<component-instance name="documents"
class="org.apache.avalon.excalibur.datasource.JdbcDataSource">
<pool-controller min="5" max="10"/>
<auto-commit>false</auto-commit>
<driver>org.gjt.mm.mysql.Driver</driver>
<dburl>jdbc:mysql:localhost/mydb</dburl>
<user>test</user>
<password>test</password>
</component-instance>
<component-instance name="security"
class="org.apache.avalon.excalibur.datasource.JdbcDataSource">
<pool-controller min="5" max="10"/>
<auto-commit>false</auto-commit>
<driver>org.gjt.mm.mysql.Driver</driver>
<dburl>jdbc:mysql:localhost/myotherdb</dburl>
<user>test</user>
<password>test</password>
</component-instance>
</component>
<component
role="org.apache.bizserver.docs.DocumentRepository"
class="org.apache.bizserver.docs.DatabaseDocumentRepository">
<dbpool>documents</dbpool>
</component>
<component
role="org.apache.bizserver.docs.GuardianComponent"
class="org.apache.bizserver.docs.DocumentGuardianComponent">
<dbpool>security</dbpool>
<policy file="/home/system/document.policy"/>
</component>
</my-system>
|
The root element can be anything you want. You will notice that we
now have several Components defined. We have our familiar
DocumentRepository class and GuardianComponent class, as well as a
couple of Excalibur DataSourceComponent classes. In addition, now we
have some specific configuration information for our Guardian
Component. In order to read that information into your system,
Avalon Framework provides some conveniences for you:
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
Configuration systemConf = builder.buildFromFile("/path/to/file.xconf");
|
This does simplify all the code we had for hand-building the
Configuration element earlier, and it limits the amount of
information we need to explicitly know right away. We will take one
last look at our Container class and see if we really have some
savings. Keep in mind that we have five components specified (a
ComponentSelector counts as a Component), and configurations for
each of them.
class ContainerComponent implements Component, Initializable, Disposable {
ExcaliburComponentManager manager = new ExcaliburComponentManager();
public void initialize()
throws Exception
{
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
Configuration sysConfig = builder.buildFromFile("./conf/system.xconf");
this.manager.setLogger( Hierarchy.getDefaultHierarchy()
.getLoggerFor("document") );
this.manager.contextualize( new DefaultContext() );
this.manager.configure( sysConfig );
this.manager.initialize();
}
public void dispose()
{
this.manager.dispose();
}
}
|
Isn't this amazing? We have more than twice the number Components
initialized and ready for use with less than half the code (six lines
of code instead of thirteen lines of code). There is the drawback of
the Configuration file looking somewhat crazy, but it minimizes the
amount of code you have to write.
There is a lot of activity happening under the hood of the
ExcaliburComponentManager. For each "component" element in the
configuration file, Excalibur creates a ComponentHandler for each
class entry and maps it to the role entry. The "component" element
and all it's child elements are used for the Configuration of the
Component. When the Component is an ExcaliburComponentSelector, the
Excalibur reads each "component-instance" element and performs the
same type of operation as before-this time mapping to the hint entry.
Making the Configuration Pretty |
We can manage the configuration file's appearance with the use of
aliases. Excalibur uses a RoleManager to provide aliases for the
configuration system. A RoleManager can either be a dedicated
class that you create, or you can use the DefaultRoleManager and
pass in a Configuration object. If I use the DefaultRoleManager, I
will hide the role configuration file inside the jar with the rest
of the system. This is because the role configuration file is only
going to be altered by developers. Below is the interface for the
RoleManager:
interface RoleManager
{
String getRoleForName( String shorthandName );
String getDefaultClassNameForRole( String role );
String getDefaultClassNameForHint( String hint, String shorthand );
}
|
Let's take a look at how Excalibur uses the RoleManager in our
scheme. First, Excalibur will cycle through all the elements that
are direct children of the root element. This includes all
"component" elements like before, but this time when Excalibur
doesn't recognize an element name, it asks the RoleManager which
role we should use for this Component. If the RoleManager returns
null, the element and all it's child elements are ignored. Next,
Excalibur derives the class name from the role name. The last
method is to dynamically map a class name to a ComponentSelector's
child type.
Excalibur provides a default implementation of the RoleManager that
is configured with an XML configuration file. The markup is very
simple, and it hides all the extra information you don't want your
administrator to see.
<role-list>
<role
name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
shorthand="datasources"
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
<hint shorthand="jdbc"
class="org.apache.avalon.excalibur.datasource.JdbcDataSourceComponent"/>
<hint shorthand="j2ee"
class="org.apache.avalon.excalibur.datasource.J2eeDataSourceComponent"/>
</role>
<role
name="org.apache.bizserver.docs.DocumentRepository"
shorthand="repository"
default-class="org.apache.bizserver.docs.DatabaseDocumentRepository"/>
<role
name="org.apache.bizserver.docs.GuardianComponent"
shorthand="guardian"
default-class="org.apache.bizserver.docs.DocumentGuardianComponent"/>
</role-list>
|
In order to use the RoleManager, you do need to alter the
"initialize" method of our Container class. You are using the
configuration builder to build a Configuration tree from this
file. Please remember, if you are going to use a RoleManager, you
must call the "setRoleManager" method before
the "configure" method. To demonstrate how you would retrieve this
XML file from the class loader, I will demonstrate the technique
below:
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
Configuration sysConfig = builder.buildFromFile("./conf/system.xconf");
Configuration roleConfig = builder.build(
this.getClass().getClassLoader()
.getResourceAsStream("/org/apache/bizserver/docs/document.roles"));
DefaultRoleManager roles = new DefaultRoleManager();
roles.enableLogging(Hierarchy.getDefaultHierarchy().getLoggerFor("document.roles"));
roles.configure(roleConfig);
this.manager.setLogger( Hierarchy.getDefaultHierarchy()
.getLoggerFor("document") );
this.manager.contextualize( new DefaultContext() );
this.manager.setRoleManager( roles );
this.manager.configure( sysConfig );
this.manager.initialize();
|
Since we added six more lines of code, we need to see what it
bought us. Our final configuration file can be written like this:
<my-system>
<datasources>
<jdbc name="documents">
<pool-controller min="5" max="10"/>
<auto-commit>false</auto-commit>
<driver>org.gjt.mm.mysql.Driver</driver>
<dburl>jdbc:mysql:localhost/mydb</dburl>
<user>test</user>
<password>test</password>
</jdbc>
<jdbc name="security">
<pool-controller min="5" max="10"/>
<auto-commit>false</auto-commit>
<driver>org.gjt.mm.mysql.Driver</driver>
<dburl>jdbc:mysql:localhost/myotherdb</dburl>
<user>test</user>
<password>test</password>
</jdbc>
</datasources>
<repository>
<dbpool>documents</dbpool>
</repository>
<guardian>
<dbpool>security</dbpool>
<policy file="/home/system/document.policy"/>
</guardian>
</my-system>
|
As you can see, this is much more readable than how we started.
Now we can add any number of components to our system, and we won't
have to write any more code to support them.
|
|
|
|
Using the Component |
Now that we have created our Components, we will want to use them. You
access Components the same way regardless of how they were instantiated
or managed. You must implement the Composable interface to get a
reference to the ComponentManager. The ComponentManager holds all the
references to the Components you need. For the sake of our discussion,
we will assume that the ComponentManager given to us is configured in the
same manner as the final Configuration file in the last section. This
means that we have a Repository, a Guardian, and two DataSources.
Rules for Using the Component Management Infrastructure |
The Component management infrastructure requires that you release any
Component for which you have obtained a reference. The reason for this
restriction is so that the Component's resources can be properly
managed. A ComponentManager is designed for cases when you have
multiple types of Components with distinct roles. A ComponentSelector
is designed for cases when you have multiple types of Components
with the same role. Another unique aspect of the ComponentSelector is
that it is a Component by design. This enables us to get a
ComponentSelector from a ComponentManager.
There are two valid approaches for handling references to external
Components. You can obtain your references during initialization, and
release them during disposal. You may also encapsulate the Component
handling in a try/catch/finally block. Each has its advantages and
disadvantages.
Initialization and Disposal Approach |
class MyClass implements Component, Composable, Disposable
{
ComponentManager manager;
Guardian myGuard;
/**
* Obtain a reference to a guard and keep the reference to
* the ComponentManager.
*/
public void compose(ComponentManager manager)
throws ComponentException
{
if (this.manager == null)
{
this.manager = manager;
myGuard = (Guardian) this.manager.lookup(Guardian.ROLE);
}
}
/**
* This is the method that uses the Guardian.
*/
public void myMethod()
throws SecurityException
{
this.myGuard.checkPermission(new BasicPermission("test"));
}
/**
* Get rid of our references
*/
public void dispose()
{
this.manager.release(this.myGuard);
this.myGuard = null;
this.manager = null;
}
}
|
As you can see by the sample code, this is easy to follow. The
object gets a reference to a Guardian Component when it first
receives the ComponentManager. If you could be guaranteed that the
Guardian Component was ThreadSafe, then this is all that is
necessary. Unfortunately, you cannot guarantee this for the long
term. To properly manage resources, we must release the Component
when we are done with it. That's why we kept a reference to the
ComponentManager.
The main disadvantage of this approach comes into play when you are
dealing with pooled Components. The reference of the Component is
kept for the life of this object. It might not be a problem if the
object had a short life span, but if it was a Component managed by
the Excalibur component management architecture, its life span is as
long as the Component whose reference it has. What this means is
that we are essentially turning the Component's pool into a Factory.
The main advantage of this approach is that the code is very clear on
how a Component is obtained and released. You don't have to have any
understanding of exception handling.
One other nuance is that you are tying the existence of the Guardian
to the ability to initialize this object. Once an Exception is
thrown during the initialization phase of an object, you must assume
that the object is not valid. Sometimes you want to fail if a
required Component does not exist so this is not a problem. You do
need to be aware of this implication when you are designing your
Components though.
|
Exception Handling Approach |
class MyClass implements Composable, Disposable
{
ComponentManager manager;
/**
* Obtain a reference to a guard and keep the reference to
* the ComponentManager.
*/
public void compose(ComponentManager manager)
throws ComponentException
{
if (this.manager == null)
{
this.manager = manager;
}
}
/**
* This is the method that gets the Guardian.
*/
public void myMethod()
throws SecurityException
{
Guardian myGuard = null;
try
{
myGuard = (Guardian) this.manager.lookup(Guardian.ROLE);
this.criticalSection(myGuard);
}
catch (ComponentException ce)
{
throw new SecurityException(ce.getMessage());
}
catch (SecurityException se)
{
throw se;
}
finally
{
if (myGuard != null)
{
this.manager.release(myGuard);
}
}
}
/**
* Perform critical part of code.
*/
public void criticalSection(Guardian myGuard)
throws SecurityException
{
myGuard.checkPermission(new BasicPermission("test"));
}
}
|
As you can see, the code is a bit more complex. In order to
understand it, you have to understand Exception handling. This is
not necessarily a problem, because most Java developers know how to
handle them. You don't have to worry so much about the Component
life style with this approach, because we are releasing it as soon
as we no longer need it.
The main disadvantage of this approach is the added complexity of the
exception handling code. In order to minimize the complexity and
make the code more maintainable, we extracted the working code into
another method. Keep in mind that we can get the reference to as
many Components as we possibly want inside the try block.
The main advantage of this approach is that you are managing your
Component references more efficiently. Again, there is no real
difference if you are using ThreadSafe Components, but it makes a
real difference when you have pooled Components. There is a slight
overhead dealing with getting a new reference every time you use a
Component, but the likelihood of being forced to create a new
instance of the Component is minimized.
Just like the Initialization and Disposal Approach, you have to
understand a subtle nuance. The Exception Handling Approach does not
fail on initialization if the Component is missing from the manager.
As mentioned before, this is not entirely bad. Many times, you want
an object to exist, but it is not a failure if a desired Component
is missing.
|
|
Getting Components from a ComponentSelector |
For most operations, you will only need the ComponentManager. Since we
decided that we needed multiple instances of the DataSourceComponent,
we need to know how to get the instance we want. ComponentSelectors
are a little trickier than ComponentManagers because we are dealing
with hints to get the reference we need. A Component has a specific
Role, and this contract is well documented. However, sometimes we need
to select one of many Components for a Role. A ComponentSelector uses
an arbitrary object for the hint. Most of the time, the object is a
String, although you might want to use a Locale object to get a proper
internationalization Component.
In our system we have set up, we chose to use Strings to select the
correct instance of the DataSourceComponent. We even gave ourselves a
Configuration element that references the exact string we need to get
the right Component. This is a good practice to follow, as it makes it
easier on administrators of a system. It is easier for an
administrator to see a reference to another Component than it is for
them to remember magic values for the configuration.
Conceptually, getting a Component from a ComponentSelector is no
different than getting a Component from a ComponentManager. You just
have one more step. Remember that a ComponentSelector is a Component.
The ComponentManager will be set up to return the ComponentSelector
when you lookup its role. You then need to select the component from
the selector. To demonstrate, I will extend the code from the
Exception Handling Approach discussed previously.
public void myMethod()
throws Exception
{
ComponentSelector dbSelector = null;
DataSourceComponent datasource = null;
try
{
dbSelector = (ComponentSelector)
this.manager.lookup(DataSourceComponent.ROLE + "Selector");
datasource = (DataSourceComponent)
dbSelector.select(this.useDb);
this.process(datasource.getConnection());
}
catch (Exception e)
{
throw e;
}
finally
{
if (datasource != null)
{
dbSelector.release(datasource);
}
if (dbSelector != null)
{
this.manager.release(dbSelector);
}
}
}
|
As you can see, we got the reference to the ComponentSelector using the
Role specified for the Component. We followed the Role naming
guidelines outlined in a previous chapter by adding the "Selector"
suffix to the Role name. It is also perfectly acceptable to use a
static interface for all the Role names in your system to minimize the
number of String concatenation in your code.
Next, we obtained the reference to the DataSourceComponent from the
ComponentSelector. Our sample code assumed that we had already pulled
the required information from the Configuration object and placed it in
a class variable named "useDb".
|
|
Excalibur's Utilities |
This last section is included to give you an idea of the types of
Components and utilities that are included with Apache Avalon Excalibur.
These utilities are robust, and fully usable in production systems. We
do have an unofficial staging project called "Scratchpad" where we iron
out implementation details for potential new utilities. Scratchpad
utilities are of varying quality, and their use is not guaranteed to
remain the same—although you may have good experience with them.
Command Line Interface (CLI) |
The CLI utilities are used by a number of projects including Avalon
Phoenix and Apache Cocoon to process command line arguments. It
provides facilities to print help responses, and to process options by
either a short name or a long name.
|
Collection Utilities |
The collection utilities provide some enhancements to the
JavaTM Collections API. Among them is the ability
to find the intersections between two lists and a
PriorityQueue that is an enhancement to
Stack to allow the priority of objects override
the simple first in/last out Stack
implementation.
|
Component Management |
We already discussed the use of this in the previous section. This is
Excalibur's most complex beast, but it provides a lot of functionality
in just a few classes. It will make one distinction more than simple
SingleThreaded or
ThreadSafe for managing a component type:
Poolable . If a Component implements Excalibur's
Poolable interface instead of the
SingleThreaded interface, it will maintain a
pool of Components and reuse instances. Most of the time this works
great. For those last remaining times where a Component cannot be
reused, use the SingleThreaded interface.
|
LogKit Management |
The Avalon development team realized that many people wanted a simple
mechanism to build complex Logging target heirarchies. In the same
spirit as the RoleManager the team developed
a LogKitManager that can be given to the
Excalibur Component Management system meantioned above. Based on the
"logger" attribute it will give the proper Logger
object to the different Components.
|
Thread Utilities |
The concurrent package contains several classes
to assist in multithreaded programming: Lock
(a mutex implementation), DjikstraSemaphore ,
ConditionalEvent , and
ThreadBarrier .
|
Datasources |
This is modeled after the javax.sql.DataSource
class, but simplified. There are two implementations of the
DataSourceComponent : one that pools JDBC
connections explicitly, and one that uses a J2EE application server's
javax.sql.DataSource class.
|
Input/Output (IO) Utilities |
The IO utilties provide a number of FileFilter s
and other File and IO specific utilities.
|
Pool Implementations |
The Pool implementations provide a Pool for
every occasion. You have an implementation that is blazingly fast, but
only usable in one thread—which should be ok for implementing a
FlyWeight pattern. You also have DefaultPool ,
which does not manage the number of objects in its pool.
SoftResourceManagingPool decommissions objects
that exceed a threshold when they are returned. Lastly,
HardResourceManagingPool throws an exception
when you have reached the maximum number of objects. The last three
pools are all ThreadSafe .
|
Property Utilities |
The property utilities are used in conjunction with Context objects.
They give you the ability to expand "variables" in your
Resolvable object. It works like this:
"${resource}" will look for a Context value
named "resource" and substitute its value
for the symbol.
|
|
|