Jakarta main

Avalon main

About


Patterns and Design


The API


Avalon Design Documentation

Avalon Design Documentation



Introduction

In Avalon, Composable is defined as an active entity that controls or uses Components. It's best analogy is that of a musical composer. The musical composer chooses what instruments (Components) by their role in the symphony (system) and tells them which notes to play.

The Avalon Composable follows the principles of Inversion of Control, and is assigned a Component Manager. Within this section we will discuss how to look up specific Components, and then how to prepare the ComponentManager for the Composable.

The Composable has a specific contract that must be enforced for security reasons. The ComponentManager must only be set once. That means that the compose method must ignore all subsequent requests to set the ComponentManager after it is successfully set.

Finding your Component

The Component Manager

For the majority of all cases, you will need to use the ComponentManager get the instance of the Component you need. If you recall the discussion on Component Roles in the Component documentation, you already have a head start. In Avalon, Roles are defined by the work interface a Component has. A work interface is different from any other interface because it is the interface that defines the Component's Role. Composable and Component are concern interfaces because they address specific concerns about the Component.

The ComponentManager has one method to retrieve all of your Components. The lookup method will look up the Component based on the fully qualified name (FQN) of the work interface (Role). It is important to realize that the ComponentManager returns Components, and therefore you must recast the Component to the Role you need. See the following example:

final MyComponent component = (MyComponent)manager.
     lookup( "com.mycompany.myproject.MyComponent" );
        

It is important to note that Role is not the same thing as functional equivalence. In other words, if you have a MailSpooler that is functionally equivalent to a FileStore (they do the same thing), it does not mean that they perform the same Role. The FileStore is used to store objects to files, and the MailSpooler is used to temporarily store messages until they are sent. Thus they are separate roles. Sometimes you need to create a new interface name that does nothing more than allow access to alternate roles who have the same role.


The Component Selector

Sometimes you will have several Components that function in the same role. For those cases, you will use the ComponentSelector to choose the exact one you need. The best way to describe it's proper use is the scenario described here. You have several formatters that have the same Role: to take an input document and format it according to the rules in the individual Component implementations. One formatter may take a text file and remove all tabs and replace them with four spaces. Another formatter may reverse the formerly mentioned one. Yet another takes the text file and formats it for a canvas object. For the Composable, it makes no difference what the implementation does--just that it formats the text.

Using the processing chain example in the previous paragraph, we realize the unsuitability of the ComponentManager for getting the right Component. The Component addresses the concern of one Component per role. Fortunately, the ComponentSelector is a Component. That means we use the ComponentManager to lookup the ComponentSelector. The ComponentSelector is designed to choose the specific Component out of many that perform the same Role. The following code will help:

final ComponentSelector selector = (ComponentSelector)manager.
    lookup( "org.mycompany.myproject.FormatterSelector" );
final Formatter formatter = (Formatter)selector.select( myURL );
        

The selector does not discriminate against lookup keys. In that respect it acts much like a hashtable lookup. Keep in mind that the implementation of the selector does not limit you to a hashtable lookup--you can dynamically instantiate objects as well. It takes an object (a hint), and returns the specific Component based on that hint.


When you are done with the Component

Both the ComponentManager and the ComponentSelector require you to release your Component when you are done with it. The method used to do this is "release". One way of handling this is to use the try/catch/finally construct. Four your convenience, the following code can help:

MyComponent component = null;

try
{
    component = (MyComponent) manager.lookup("org.mycom.MyComponent");
    component.myMethod();
}
catch (Exception e)
{
    getLogger().debug("Error using MyComponent", e);
}
finally
{
    if (component != null) manager.release(component);
}
	

The reason for this is so that smart Component Managers that select Components from a pool can properly manage the resources.



Populating the ComponentManager

It is the responsibility of the entity that creates the Composable to give it a ComponentManager with all of the Roles populated. If you create your own implementations of the ComponentManager and ComponentSelector then you have the liberty of deciding how to populate them. Keep in mind that there are default implementations included, and you should model their behavior as much as possible.

DefaultComponentManager

The DefaultComponentManager is nothing more than a Hashtable lookup of roles and Components. It even gives you the method put to populate the ComponentManager. One feature of the DefaultComponentManager is that it can cascade. In other words, if the role is not found in this ComponentManager, the default implementation will look in the parent ComponentManager.

For the paranoid developer, the Cascading feature of the ComponentManager can be seen as a security hole as opposed to a usability enhancement. You are free to create your own implementation that does not use the Cascading feature--but you have to manually populate it with anything that would have been in the parent ComponentManager that your child Composable needs. Truth be told, there is very little risk due to the set-once contract for ComponentManagers. The method is never exposed to hostile agents before the ComponentManager is set.


DefaultComponentSelector

The DefaultComponentSelector again is simply a Hashtable selection of Components based on hints. It gives the method put to populate the ComponentSelector. The ComponentSelector does not have the cascading feature of the ComponentManager, nor should it. A ComponentSelector simply holds a number of Components that implement the same role--there really is no need to cascade.

After the ComponentSelector is populated, you must put it in the ComponentManager. Please use the role of the Component you are selecting, not the role of the selector itself. An acceptable convention is to add the "Selector" name to the end of the Role you are looking up. Just be consistent.





Copyright ©1999-2002 by the Apache Software Foundation. All Rights Reserved.