avalon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Robert Mouat <rob...@mouat.net>
Subject [proposal] custom component lookup
Date Thu, 20 Jun 2002 21:28:54 GMT
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:avalon-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:avalon-dev-help@jakarta.apache.org>


Mime
View raw message