Hi All,
Hope all is well!
Last week I started a thread about easing the implementation
of custom markers within Avalon. After a bit of R&D I've come up with
a potential solution for review/discussion.
I've attached the required code changes to this email in a tgz.
The changes relate to the Fortress source base, and the attached
archive should untar directly into a fresh Fortress's src dir from
current CVS.
(I didn't do a diff since there's a reasonable number of new files, and
I didn't really want to duplicate the Fortress code into scratchpad
(although I can do this if that would be easier for people), so I hope
the form of an attachment is ok).
The attached code has been tested with the ContainerProfile
testcase using some simple components, so some parts may be missing.
At this stage it would be great to get some feedback about it so I/we
can improve/change/modify/enhance the idea to get to something
production quality.
To recap on the previous thread - the motivation was to make it easier
to add a new custom marker to an avalon system without having to
subclass CM's/CS's, and to be able to define where the new custom
marker should be checked for during a component's lifetime.
The basic idea is to introduce a new interface called ComponentMarker:
public interface ComponentMarker
{
/**
* Create, called when the given component is being
* instantiated.
*
* @param component a <code>Component</code> instance
* @param context a <code>Context</code> instance
* @exception Exception if an error occurs
*/
void create(Component component, Context context)
throws Exception;
/**
* Destroy, called when the given component is being
* decomissioned.
*
* @param component a <code>Component</code> instance
* @param context a <code>Context</code> instance
* @exception Exception if an error occurs
*/
void destroy(Component component, Context context)
throws Exception;
/**
* Access, called when the given component is being
* accessed (ie. via lookup() or select()).
*
* @param component a <code>Component</code> instance
* @param context a <code>Context</code> instance
* @exception Exception if an error occurs
*/
void access(Component component, Context context)
throws Exception;
/**
* Release, called when the given component is being
* released (ie. by a CM or CS).
*
* @param component a <code>Component</code> instance
* @param context a <code>Context</code> instance
* @exception Exception if an error occurs
*/
void release(Component component, Context context)
throws Exception;
}
Component marker defines various methods that can be called on a
given component during 1 of the 4 phases during that component's
lifetime. ie:
o instantiation
o access
o release
o decommissioning
These methods are essentially callbacks.
To implement a marker for a particular phase, a concrete class is
created that implements the above interface, performing some
operation on the given component in the appropriate method. For
example, LogEnabled would implemented like this:
public class LogEnabledMarkerImpl extends AbstractMarker
{
/**
* <code>create</code> method, enables logging on the given
* component
*
* @param component a <code>Component</code> instance
* @param context a <code>Context</code> instance
* @exception Exception if an error occurs
*/
public void create(Component component, Context context)
throws Exception
{
final Configuration config = (Configuration)
context.get(COMPONENT_CONFIGURATION);
final String logger = config.getAttribute("logger", null);
final LoggerManager logManager = (LoggerManager)
context.get(LOGGER_MANAGER);
final LogEnabled logEnabled = (LogEnabled) component;
if (null == logger)
logEnabled.enableLogging(logManager.getDefaultLogger());
else
logEnabled.enableLogging(logManager.getLoggerForCategory(logger));
}
}
Since LogEnabled only applies to when the component is being
instantiated, only the 'create' method needs to be implemented
(interfaces like Startable require more).
Implementing a custom marker is done in the same way, simply by
creating a class that implements ComponentMarker, or extends from a
helper class AbstractComponentMarker (which just provides empty
implementations of all 4 methods).
All of the available ComponentMarker classes are then listed in a
configuration file, eg. markers.xml as follows:
<?xml version="1.0"?>
<!-- Component Management Configuration
This configuration file defines the marker interfaces that every component in
this container will be tested for during the appropriate phases of a components
life.
The life of a component is broken down into 4 phases:
1. creation - when a component is instantiated by a ComponentFactory class.
2. access - when a component is accessed by a ComponentHandler class.
3. release - when a component is released by a ComponentHandler class.
4. destroy - when a component is decomissioned by a ComponentFactory class.
A component will be created and destroyed only once during it's lifetime, but
accessed and released potentially many times, depending on the particular type
of handler that is responsible for the component.
For each phase, a configuration section is defined below, which lists the marker
interfaces which should be tested for and executed in order, on each component
being used in this system.
A 'marker' block defines an interface to be tested for by the 'interface'
attribute,
and an 'action' class to be executed should the test succeed. 'action' classes
must implement the ComponentMarker interface, which defines methods that will be
called at each phase.
-->
<component-management>
<create>
<marker interface="org.apache.avalon.framework.logger.LogEnabled"
action="org.apache.excalibur.fortress.markers.impl.LogEnabledMarkerImpl"/>
<marker interface="org.apache.avalon.framework.context.Contextualizable"
action="org.apache.excalibur.fortress.markers.impl.ContextualizableMarkerImpl"/>
<marker interface="org.apache.avalon.framework.component.Composable"
action="org.apache.excalibur.fortress.markers.impl.ComposableMarkerImpl"/>
<marker interface="org.apache.avalon.framework.configuration.Configurable"
action="org.apache.excalibur.fortress.markers.impl.ConfigurableMarkerImpl"/>
<marker interface="org.apache.avalon.framework.parameters.Parameterizable"
action="org.apache.excalibur.fortress.markers.impl.ParameterizableMarkerImpl"/>
<marker interface="org.apache.avalon.framework.activity.Initializable"
action="org.apache.excalibur.fortress.markers.impl.InitializableMarkerImpl"/>
<marker interface="org.apache.avalon.framework.activity.Startable"
action="org.apache.excalibur.fortress.markers.impl.StartableMarkerImpl"/>
</create>
<access>
</access>
<release>
</release>
<destroy>
<marker interface="org.apache.avalon.framework.activity.Disposable"
action="org.apache.excalibur.fortress.markers.impl.DisposableMarkerImpl"/>
</destroy>
</component-management>
This file is read by/configures a MarkerManager class, which the
ComponentFactory and ComponentHandlers reference. When a component
is requested, the appropriate markers are tested for, in order, on the
particular component and executed if implemented.
Adding a custom marker can be done by adding a new <marker../> tag
to the above file, or by modifying the above configuration
programatically at runtime (programatic modification is not yet done).
In the attachment there's also a few changes to the
ContextBuilder/Manager classes to specify the location of this
file, and to have a MarkerManager object set on the component handlers.
A few open issues so far:
o Validation that the standard avalon interfaces are not tampered
with. ie. reordered/removed/etc.
o Performance penalty incurred due to increased use of
dynamics/reflection. This new code *does* decrease performance
compared to the previous 'instanceof LogEnabled, instanceof
Contextualizable' way. There are probably ways to improve
performance though that need to be investigated.
o Use of context. Currently everything that is needed within a
ComponentMarker instance needs to be retrieved from the
supplied Context object.
This means the context has to be populated with all required data
before components are instantiated (or at least populated with objects
that can provide that data), unless static methods, etc are used.
This is though as far as I can tell, only an issue for custom markers.
Ok, hope the explanation wasn't too terse. The code should
illustrate everything though, feel free to ask any questions, etc.
Would be great to hear what you all think.
Cheers,
Marcus
--
.....
,,$$$$$$$$$, Marcus Crafter
;$' '$$$$: Computer Systems Engineer
$: $$$$: ManageSoft GmbH
$ o_)$$$: 82-84 Mainzer Landstrasse
;$, _/\ &&:' 60327 Frankfurt Germany
' /( &&&
\_&&&&'
&&&&.
&&&&&&&:
markers.tgz
Description: GNU Unix tar archive
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
