here is a proposal that will allow us to:
- have a well defined relationship between the container and
component managers [though it doesn't touch on the issue of
component resolving]
+ you could write component managers that will work in any
container - and be specified at assembly time.
+ these new component managers will work alongside the existing
component managers (allowing for a gentle upgrade path).
- create dynamic proxies, for example:
+ tracing of component level method calls.
+ aaa (authentication, authorisation, and accounting), an aaa
proxy could intercept component level method calls.
+ release() via the VM's garbage collector.
+ sessions could be implemented this way (with only slight
modifications from my recent email).
- possibly satisfy some of the need for custom markers (as long as
they are called after initialize). [sessions would probably fit this
category]
- chain together any of the above (at the whim of the assembler).
this will work with version 4 (and 5) of the framework and shouldn't
break anything currently in existence.
==== proposal: CustomLookup interface ====
the proposal is simply that we add the following interface to the
framework:
interface CustomLookup
{
public Object lookup();
}
with the contract:
if a component implements the CustomLookup interface then upon
ServiceManager.lookup() a reference to the component is not
returned, but instead the result of the CustomLookup.lookup()
method is returned.
e.g. inside the ServiceManager the following:
return component;
could be replaced with:
if ( component instanceof CustomLookup )
{
return ((CustomLookup) component).lookup();
} else {
return component;
}
Note: the CustomLookup should probably have a release() method that
is deprecated/removed with the ServiceManager.release().
Note: the CustomLookup.lookup() has no args (to prevent it from
being used as a selector/directory/resolver)
I'd also like to make the following recommendation:
in the absence of any other lifestyle indicator a component
implementing CustomLookup should be a singleton, otherwise a new
instance should be created upon every lookup.
Note: the container will still have to be able to handle these two
lifestyles (single and per-lookup) but all others can be managed by
CustomLookup components.
It shouldn't take much effort to implement this change.
==== example: component manager ====
note: component managers get the benefits of the lifecycle methods...
for simplicity: not dealing with synchronization, exceptions, and
assuming that release() will get called.
class PoolingComponentManager
implements CustomLookup, Parameterizable, Composable, Initializable
{
static final DEFAULT_SIZE = 5;
String m_role;
int m_size;
List m_pool;
ComponentManager m_cm;
public void parameterize( Parameters parameters )
{
m_size = parameters.getParameterAsInteger( "size", DEFAULT_SIZE );
m_role = parameters.getParameter( "role", null );
}
public void Compose( ComponentManager cm )
{
m_cm = cm;
}
public void initialize()
{
m_pool = new ArrayList( m_size );
}
public Object lookup()
{
if (! m_pool.isEmpty() )
return m_pool.remove( 0 );
else
return m_cm.lookup( m_role );
}
public void release( Object component )
{
if ( m_pool.size() < m_size )
{
if ( component instanceof Recyclable )
((Recyclable) component).recycle();
m_pool.add( component );
}
else
m_cm.release( component );
}
}
To use the PoolingComponentManager on a component X the assembler
could do the following...
1. configure the pooling component manager so that it has parametes
role=X-role size=5
2. configure the component locater so that a lookup that would have
resolved to X now resolves to the pooling component manager. And
that when the pooling component manager looks up X-role it gets X.
Now whenever a client makes a request that would resolve to X, the
request will go to the pooling component manager which will return a
component from its pool or request X-role from the ComponentManager
(if the pool is empty).
==== example: dynamic proxy ====
here is an example of how a tracing proxy could be implemented (minus
exception handling and synchronization)...
class TracingProxy extends AbstractLogEnabled
implements CustomLookup, InvocationHandler, Composable, Parameterizable
{
String m_role;
String m_name;
ComponentManager m_cm;
Map m_ids = new WeakHashMap();
int count = 0;
public void parameterize( Parameters parameters )
{
m_role = parameters.getParameter( "role", null );
m_name = parameters.getParameter( "name", "tracingProxy" );
}
public void compose( ComponentManager cm )
{
m_cm = cm;
}
public Object invoke( Object proxy, Method method, Object[] args )
throws Throwable
{
Object ret;
Object id = m_ids.get( proxy );
info( "Enter: " + method + " for " + m_name + "-" + id );
try
{
ret = method.invoke( m_component, args );
}
catch ( InvocationTargetException ite )
{
warn( "Error in: " + method " for " + m_name + "-" + id, ite );
throw ite.getTargetException();
}
info( "Leave: " + method + " for " + m_name + "-" + id );
return ret;
}
public Object lookup()
{
Object component = m_cm.lookup( m_role );
Class[] interfaces = component.getClass().getInterfaces();
ClassLoader classLoader = component.getClass().getClassLoader();
Object proxy = java.lang.reflect.Proxy.newInstance(
classLoader, interfaces, this
);
m_ids.put( proxy, new Integer( ++m_count ) );
return proxy;
}
}
To activate the tracing on a component X the assembler could do the
following...
1. configure the tracing proxy so that it has parameters:
role=X-role, name=X-trace
2. give the tracing proxy its own logger.
3. configure the component locator so that a lookup that would have
resolved to X now resolves to the tracing proxy. And that whenever
the tracing proxy looks up X-role it gets X.
Now whenever a client makes a request that would resolve to X they
will get a proxy that will log all method calls, before passing them
on.
====
a SessionManagingProxy proxy can be created in a similar fashion to my
recent email, with the change that the Proxy now runs the show (and
the SessionEnabled component should probably let m_SessionManager
default to an instance of SimpleSessionManager in case
setSessionManager() isn't called).
==== issues ====
- it is now possible for the assembler to specify cyclic
dependencies when chaining Customlookup components together, this
may cause lookup() to hang.
- unless you have something like:
public Object lookup() { return this; }
no other component will ever get a direct reference to the
CustomLookup component. So I'm wondering if it is still correct to
refer to it as a component... perhaps meta-component might be
better... or since the dynamic proxies could fall into a broad
definition of component management, perhaps manager-component...
- I don't know if 'CustomLookup' is a great name so feel free
to suggest others... MetaComponent?
Robert.
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>