You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by Berin Loritsch <bl...@apache.org> on 2002/01/14 14:37:03 UTC

Recapping Discussion on Component Accessing

Now is the time to review a summary of how the Components are accessed in a
system.  There have been alot of ideas thrown around and clarifications.  In
the end, I have come to a couple conclusions:

1) There is still confusion over what constitutes a Component (I will have to
    write a book it seems ;P ).
2) Some people are not satisfied with the current simply role based access.

We have all agreed that Poolable is an interface we want to remove.  Component
is one that most people want to remove--and my reservations concern people
abusing the concept of a Component.  The SingleThreaded and Threadsafe discussion
provided a couple of solutions.

There are two approaches that have been approached:

1) Merge CM and Context
2) Use Context as CM and create a new ServiceManager

However, the issue I see is one of muddying contracts.  What was proposed as a
ServiceManager is nothing more than a ComponentManager.  What was proposed as
the new CM was nothing more than a Context with a release() method attached.

What was not addressed to my liking was that of how a Component was to know
how to manage it's threadsafety concerns (whether one instance could be shared
accross all threads or it needed to have a unique instance per thread).

The approach of writing a "factory" for each component is time-consuming, but
it does address how to control the lifestyle policy of a Component.  The major
issue is one of interface.  For a general purpose container to be written,
the Factories would have to specify a naming convention (i.e. like URLConnectionFactory)
as well as use a predefined interface.  The following would be an example:

interface ComponentFactory extends ObjectFactory, Disposable
{
     setUp(Logger log, Context context, Configuration config, ComponentManager manager);
}

Any necessary set up to the ComponentFactory happens in the initial setUp.  Even
if a Component only uses one or two of the objects passed in, all must be specified
to allow automatic set up of an environment based on configuration.

In essence, however, we will have two layers of factories.  One to create the initial
object (for the pool or singleton implementations), and one for the overall component
access point.  If we have two implementations (one for ThreadSafe and one for SingleThreaded),
all Components only have to extend one of the implementations for their ComponentFactory.
This will lead to a number of classes that look like this:

class DataSourceComponentFactory extends ThreadSafeComponentFactory {}

class JaxpParserFactory extends SingleThreadedFactory {}

While this does lead to predictable results for whatever component we are needing,
it adds alot of clutter to a system.  Extending a class just to change it's name
seems wasteful to me.  However, it does get rid of the need for SingleThreaded or
ThreadSafe as marker interfaces.  (This is a simple reworking of the ComponentHolder
stuff in ECM).


Moving on to the Context ~ ComponentManager discussion.  I must admit that there are
some unique possibilities with this approach that cannot be done with the current
ComponentManager architecture.  By employing three or four layers of Context objects,
you can easily represent a system fully.  Those layers are:

1) Application
2) Workspace (not all systems need this, but it is equivalent to Servlet Context)
3) Session (conversational state accross request boundaries)
4) Request (temporary state for the life of one request)

However, if we are to access Components through this approach, we must consider thread
interaction.  The Application and Workspace layers act as a global context, and that
carries the same requirements of a ComponentManager.  However, the Session object has
needs that are in between global context and request context.  I will cover the context
objects in reverse order.

Most request models assume that a particular request is processed in only one thread
at a time.  This is true for both event drivent architectures as well as for thread
per connection architectures.  For each slice of time, a request is processed by only
one thread at a time--even though the particular thread handling the piece of the
request may change during the life of the request.  We can leverage that fact to hide
a token for releasing objects from a request.

In essence, the Request Context would be able to have an interface like this:

interface RequestContext extends Context
{
     void releaseAll();
}

Internally, the RequestContext would know that all Components requested by the Context
only need to be requested once (and cache the instance for the life of the request).
When releaseAll() is encountered, all Components requested so far would be released
by that token.  The Request Context would have to be passed with every call to a Component:

cocoon.process(myRequestContext, uri);

The process method would then use the myRequestContext to access what it needs:

Sitemap sitemap = (Sitemap) requestContext.get(Sitemap.ROLE);
sitemap.process( requestContext, uri );

Finally, the container that originally sent the request would call the releaseAll()
method, and it would go through a list of all Components requested, and release them
if they have not already been released.

Moving to the Session Context, it follows the same principles as the request object.
The only problem is that it is not outside the realm of possibility that two requests
may be processed for the same session at any one time.  As an example, consider a
resource that takes 20 seconds to generate and an impatient web user.  The web user
might make the once every 2 seconds until he sees a result.  That means there are
10 simultaneous requests for the same resource.

We cannot rely on the Context to hide the token to release the Components retrieved
for the request.  We also cannot cache the instances we requested unless we keep
track of the tokens for each request.

One way of managing this relationship is for the Session Context to generate the
RequestContexts used.  The Session Context would be able to assign the token to
the specific RequestContext--and the RequestContext would defer component lookup
to the Session Context.  When the releaseAll() is encountered on the RequestContext,
any Components used by that request are released on the Session Context.

Technically speaking, we can supply the same relationship to the workspace context.
In essence, we can set up a WorkspaceContext like this:

interface WorkspaceContext extends Context
{
     SessionContext getSessionContext( Object sessionid );
}

interface SessionContext extends Context
{
     RequestContext getRequestContext( Object requestid );
}

interface RequestContext extends Context
{
     void releaseAll();
}

If you ask about the ApplicationContext, that is taken care of by the simple Context
interface.  Each Component get() operation will defer to the parent context until
it is resolved.  It is highly unlikely that a requestContext would explicitly declare
new Components to be used and would only contain certain values to control the request.

For example, Each context layer is identified by an id--which can be used to resolve
component mapping.  For instance:

Object get( Object key )
{
     if (key.equals(Generator.ROLE))
     {
         return m_parent.get(key + this.get("uri"));
     }
     Object retVal m_map.get(key);

     if ( null == retVal )
     {
         return m_parent.get(key);
     }

     return retVal;
}

While this leads to great flexibility, and the ability to alter the specific component
based on the Context information, it also adds complexity that is not needed in every
situation.  It would be able to hide the token used for releasing Components, as well
as remove the need for a separate ComponentManager and Context.  It also allows the
Context to handle some mapping for you.

However, there are some caveats with it as well.  For instance, in many systems the
mapping logic is either handled by a parent, or program logic in a separate component.
Whether that Component should be a ComponentManager or not can also be up for debate.

The heirarchical Context approach does provide a unified view of a Context hierarchy
in a system.  However, it also is very server centric.  We all know that Avalon is
flexible enough to be used in many different contexts.  Not to mention, the hierarchical
Context appraoch is too complex for environments where a simple CM would do.

In a web environment, the approach would work very well when all you want to do is
something like this:

Pipeline pipeline = (Pipeline) sitemap.process( requestContext, uri );
pipeline.sendResponse( outputStream );

requestContext.releaseAll();

The sitemap takes care of the request processing logic to build the pipeline, and you
deal with the simple interface.  It is all about simplifying the API for the developer.

However, I believe the Context was meant to be a light-weight object, and this adds
considerable bulk to the semantics of the Context.  To this end, some might say that
we just need JNDI--but that is just too much IMO.

Perhaps the heirarchy I outlined is too domain specific.  I would like your comments
on it, as it does have some merits and some drawbacks.

-- 

"They that give up essential liberty to obtain a little temporary safety
  deserve neither liberty nor safety."
                 - Benjamin Franklin


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: Recapping Discussion on Component Accessing

Posted by Peter Donald <pe...@apache.org>.
On Wed, 16 Jan 2002 00:52, Berin Loritsch wrote:
> Between your's and Leo's comments, I believe we came full circle and
> established that the contracts we have now are adequate.  We have not
> resolved whether the Component interface can be gotten rid of, and for the
> reasons of the confusion of what a Component is, I think it would be
> detrimental to remove it.

Im not so sure but I have no real need to kill it.

> They are useful marker interfaces for systems that take advantage of that
> information.  I say they need to stay, are part of the Framework, because
> the LifeStyle is just as important as the LifeCycle.  For 99.9% of all
> situations, a Component should be either pooled or shared.

99% aye? Thats funny - I have never used Poolable comonents in applications 
dev - even when they are not sharable :)

-- 
Cheers,

Pete

------------------------------------------------------------
 militant agnostic: i don't know, and you don't know either.
------------------------------------------------------------

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


RE: Recapping Discussion on Component Accessing

Posted by Leo Sutic <le...@inspireinfrastructure.com>.

> From: Berin Loritsch [mailto:bloritsch@apache.org]
> 
> Between your's and Leo's comments, I believe we came full circle 
> and established
> that the contracts we have now are adequate.  We have not 
> resolved whether the
> Component interface can be gotten rid of, and for the reasons of 
> the confusion
> of what a Component is, I think it would be detrimental to remove it.

I am +1 on this. As the interface does not have a contract attached to it
in the same way as the other methodless marker interfaces, I see
no reason for it nor against it from an architectural POV, but as you 
say, it helps emphasize that a component isn't just any object.

> The only grievance I have is that Recyclable is in a different 
> package than
> ThreadSafe and SingleThreaded.

True. It, and Poolable, should be in framework.

I consider framework to be the core of Avalon, with Excalibur
being the default implementations of the contracts set forth in
framework.

By putting Poolable/Recyclabe in Excalibur, we get components
that only works optimally with one implementation of Avalon.

Now, I can accept that, for example, we may have a 
org.apache.xp.SupportsWindowsXPTweaks interface to
mark components that can be made to work much better in XP,
but something as fundamental as SingleThreaded/ThreadSafe/Poolable
should be in framework.

Transactional handling of components:
-------------------------------------

This was quite interesting.

Having a 
  Component lookup (Object token, String role);
  releaseAll (Object token);

in addition to the existing methods in ComponentManager would be 
cool. I see the tokens much like one would use synchronization
objects - suppose my component keeps some components for its
whole lifetime, and some just on a per-request basis:

public class MyComponent {

  private final Object lifetimeToken = new Object ();
  ...
  private LifetimeComponent lcCom = null;
  ...

  public void compose (ComponentManager manager) throws ComponentException {
    lcCom = manager.lookup (lifetimeToken, LifetimeComponent.ROLE);
  }

  public void handleRequest (Request req) {
    RequestComponent rqCom = null;
    try {
      rqCom = manager.lookup (req, RequestComponent.ROLE);
      ...
    } catch (Exception e) {
    } finally {
      manager.releaseAll (req);
    }
  }

  public void dispose () {
    manager.releaseAll (lifetimeToken);
  }
}

/LS

 


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: Recapping Discussion on Component Accessing

Posted by Berin Loritsch <bl...@apache.org>.
Peter Donald wrote:

> On Tue, 15 Jan 2002 00:37, Berin Loritsch wrote:
> 
>>Perhaps the heirarchy I outlined is too domain specific.  I would like your
>>comments on it, as it does have some merits and some drawbacks.
>>
> 
> I would classify it as so ;)


Between your's and Leo's comments, I believe we came full circle and established
that the contracts we have now are adequate.  We have not resolved whether the
Component interface can be gotten rid of, and for the reasons of the confusion
of what a Component is, I think it would be detrimental to remove it.

It is trivial to wrap a third party "component" with an Avalon Component, as
demonstrated quite amply by Cocoon.  Furthermore, if you need to use a third
party "component" in your existing framework, sometimes you need to make it
work with the interfaces you already have defined (i.e. am I always going to
use it as a Generator?).

The only grievance I have is that Recyclable is in a different package than
ThreadSafe and SingleThreaded.

They are useful marker interfaces for systems that take advantage of that
information.  I say they need to stay, are part of the Framework, because
the LifeStyle is just as important as the LifeCycle.  For 99.9% of all
situations, a Component should be either pooled or shared.


-- 

"They that give up essential liberty to obtain a little temporary safety
  deserve neither liberty nor safety."
                 - Benjamin Franklin


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: Recapping Discussion on Component Accessing

Posted by Peter Donald <pe...@apache.org>.
On Tue, 15 Jan 2002 00:37, Berin Loritsch wrote:
> Perhaps the heirarchy I outlined is too domain specific.  I would like your
> comments on it, as it does have some merits and some drawbacks.

I would classify it as so ;)

-- 
Cheers,

Pete

---------------------------------------------------
Murphy's law - "Anything that can go wrong, will." 
(Actually, this is Finagle's law, which in itself 
shows that Finagle was right.)
---------------------------------------------------

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


RE: Recapping Discussion on Component Accessing

Posted by Leo Sutic <le...@inspireinfrastructure.com>.
> From: Berin Loritsch [mailto:bloritsch@apache.org]
>
> There are two approaches that have been approached:
>
> 1) Merge CM and Context
> 2) Use Context as CM and create a new ServiceManager

For this email I'll use the definitions used for Avalon 4. That is,
a Component provides a service. (URL != Component, for example).

Regarding Berin's two layers of factories - the instance-production
layer and the pool/thread-safe holder layer, I'll refer to the lower
layer as the Component Factory and the upper as the Component Handler.

> What was not addressed to my liking was that of how a Component
> was to know how to manage it's threadsafety concerns (whether one
> instance could be shared accross all threads or it needed to have
> a unique instance per thread).

The problem here is, I think, whether ThreadSafe, SingleThreaded and
Poolable captures every possible way of managing a component
with respect to its thread-safety. If they do, then the other problems
you list disappear.

If they do (and yes, I am in favor of the Poolable interface), then
I think we should:

 1) Add a bool recycle () method to Poolable, removing it from
    Recyclable. The method returns true upon successful recycling,
    and false when not. Poolable extends SingleThreaded, for
    graceful degradation where the CM can not create a pool.

 2) Move the new Poolable to framework. If Poolable is as fundamental
    as ThreadSafe, then it belongs in framework, not in Excalibur.

 3) Stick with our current ComponentHandlers.

 4) Add an optional "handler" attribute to the configuration, to
    handle components that do not conform to Avalon standards.
    For example:
       <component role="..." class="my.ComponentImpl"
handler="my.ComponentHandler"/>
    where my.ComponentHandler extends
org.apache.avalon.excalibur.component.ComponentHandler.
    If none is specified, a handler is selected via marker interfaces.

 5) Move org.apache.avalon.excalibur.component.ComponentHandler to
framework.
    If not, then there is no guarantee that your component will work
    with any implementation of Avalon.

BTW: Why this rush to get rid of marker interfaces? C# added custom
properties that could be attached to any object as a feature,
and here we are regressing, throwing away a great feature of Java -
namely that classes can contain metadata about themselves.

The only reason for getting rid of the marker interfaces is
if they become too many. But I think the set {SingleThreaded,
Poolable, ThreadSafe} covers 99% of the possible cases.

Allowing one to override the Component Handler class (and thus
the Component Factory) should be enough for the remaining 1%.

> Moving on to the Context ~ ComponentManager discussion.  I must
> admit that there are
> some unique possibilities with this approach that cannot be done
> with the current
> ComponentManager architecture.  By employing three or four layers
> of Context objects, you can easily represent a system fully.

You can easily represent a *request-response* system fully.

I would vote -1 on this, if I could. I'm trying to apply
Avalon patterns to games and simulation programming, and
while I could possibly map the layers you list to my application
it would be quite forced and far from optimal.

> However, if we are to access Components through this approach,

Berin, by "access" I suppose you are talking about the upper
layer of factories, the Component Handlers. I think we're really
messing up the contracts here. Basically, we access components
through a ComponentManager.

> While this leads to great flexibility, and the ability to alter
> the specific component
> based on the Context information

Is this not exactly what a ComponentSelector does?

/LS


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>