cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Berin Loritsch" <blorit...@apache.org>
Subject [Design] ContainerManager is under fire--let's find the best resolution
Date Thu, 06 Jun 2002 14:55:44 GMT
If anyone is subscribed to the JAMES list, please include them on the
conversation.  This is an important issue that requires the attention
of everyone involved.  The next generation of the ComponentManager will
be simplified from the current implementation, which will force some
projects to change the way they design their systems and interfaces.

Therefore, I suggest we come together on how we lookup and use our
components.  Currently there are three trains of thought: one inspired
by the ECM (monolithic), one inspired by Phoenix(only lookup), and one
inspired by Fortress(in the middle).

The first order of business is that we need to be clear on our
terminology.

* Component -- a managed resource that is resolved at runtime (late
binding)
* Container -- a resource manager that resolves component instances
* Lookup Mechanism -- the interface that the client uses to lookup
components
* Client -- the code that uses components

I would love to give an illustration, but its copyrighted...

Anyway, here we go:

MONOLITHIC DESIGN
-----------------
No other way to say it than its bad.  By merging all the concerns into
one
entity, we are tempted to do to add new methods to the lookup mechanism.
One example that was probably (in retrospect) ill advised was the
"release()"
method on the CM.  At the time both Giacomo and I argued that the only
way
a container would have the knowledge that a client is done with a
component
is to explicitly say so.  Having seen what has happened in Cocoon's
explosion
of component types and interfaces, I can see there are better ways of
doing
things.  The section at the end will identify exactly what I mean.

SEPARATED CONCERNS
------------------
We have all been indoctrinated at how good this is.  We know because we
use it.
Avalon components are much less painful to develop than EJBs, and a
large part
of that is because EJB design uses a monolithic mindset and Avalon uses
SOC.
A container is a container.  A lookup mechanism is a lookup mechanism.
There
is nothing more that needs to be said about that other than what is
philisophically
separate should be separate in code.

SOMEWHERE IN THE MIDDLE
-----------------------
I started on Fortress with the mindset of correcting the most heinous
wrongs
committed by the ECM.  A Container is a Container, and a lookup
mechanism is
a lookup mechanism.  However, in an attempt to maintain as much
compatibility
with the ECM as possible it inherited a couple of the shortcommings.
However,
correcting those wrongs would require changing the way we write
components.

The origional ComponentManager Interface
----------------------------------------

interface ComponentManager
{
    Component lookup( String role ) throws ComponentException;
}

That was it.  Nothing more.  The whole problem started with the need for
named
components, or components resolved by a hint.  As a ressult we invented
the
ComponentSelector (actually in Cocoon there was a
"NamedCOmponentManager" that
resolved "NamedComponents").  The Avalon team did correctly deduce that
there
was no real need for NamedComponents.  The name, or hint is a function
that
the container assigns to a component--not something inherent in the
component
itself.

Then cocoon needed a mechanism for pooled components.  However there was
no
way to return a component instance to the container.  Peter tried to
tell us
that it can be done, and that it should not be done at the CM level.  In
retrospect, I am sorry we prevailed.  As a result, we have the
following:

interface ComponentManager
{
    Component lookup( String role ) throws ComponentException;
    boolean hasComponent( String role );
    void release( Component comp );
}

and

interface ComponentSelector
{
    Component lookup( Object hint ) throws ComponentException;
    boolean hasComponent( Object hint );
    void release();
}

                             -o0o-

In an attempt to remove the ComponentSelector abstraction,  I came up
with
something that would be a high performance CM interface.  It used a
Request
or Prototype object to resolve the request for a component ahead of
time.
As a result, we could safely remove the requirement for the
ComponentException.
At first I thought it would be a step forward, and I pitched it to the
Avalon list.  When I got some opposition from Peter and Leo, I pitched
it to
Stefano to see if he could recommend anything.

Surprisingly enough, Stefano's reply was simply

"Ok, just one (maybe stupid) question: what's wrong in something like
this:

interface ComponentManager {
     Component getComponent(String role);
     Component getComponent(String role, Object hint);
     ...
}
"

with a further clarification of

"I currently disklike the abuse of (non-runtime) exceptions that Avalon
throw
to indicate things that are not found. A try/catch requires the JVM to
save
the state of the execution (sort-of an internal
continuation) and this is a very expensive process.

"The code above is much more performant and friendly in that respect,
even if
I still don't understand the need for those 'request' stubs."

Which raises two very good points.  How should we handle the CM
interface,
and do we need exceptions?

As a result, I would like to go back to grass roots, with a couple
modifications.

Here is the interface for the CM:

interface ComponentManager
{
    Object lookup(String role);
    Object lookup(String role, Object hint);
    boolean exists(String role);
    boolean exists(String role, Object hint);
}

If you notice, there are four distinct differences from the current
ComponentManger:

1) We return Object instead of Component.  This merges the lessons
learned from
   the ServiceManager discussions about accessing legacy components that
are not
   able to emplement the Component interface.

2) We remove all *declared* exceptions.  If there is something wrong, it
is more
   appropriate to throw a RuntimeException.

3) We add an additional lookup and hasComponent method (role, hint
combination).

4) We remove the release() method altogether.


Now, I forsee few raised flags with the first three changes.  They
simplify our
lives, and help make truly performant code.  The role/hint combo takes
its cue
from a common construct in Java, used in JSSE, i18n resolution, and many
other
factories.  Basically there is a default implementation, and the hint is
used
to let the "factory" choose a different implementation than the default
if need
be.  It also lets us get rid of the ComponentSelector.

In this case, it is a hint to the Container that I not only want a
ConnectionManger,
I want one that handles TLS connections.  That way, we can look it up in
one call,
with no string concatenations:

ConnectionManager conn =
   (ConnectionManager) manager.lookup( ConnectionManager.ROLE, "TLS" );

There is little confustion, and if the hint is not supplied, the CM
returns the
default implemenatation.  It also lets us resolve bundles using a
Locale:

XMLBundle bundle = (XMLBundle) manager.lookup( XMLBundle.ROLE,
Locale.getInstance("US") );

Or something like that...

What will require some convincing--esp. to the Cocoon crew is the
removal of
the release() method.  To that end, read the last section.


                   -o0  What needs to change 0o-

In order for a container to manage component instances using pools or
per/lookup
policies, we need to change the way we think about our components.
Consider the
DataSourceComponent as a prototype.  With the DataSourceComponent, the
client
code does not care how the Connection object came into existance.  It
does not
care that it has been used 100 times before.  All it cares about is that
the
Connection is ready to be used now.  The DataSourceComponent does not
have a
release method, but yet it manages to pool the Connection instances.

The secret is the proxy layer.  The proxy layer is a wrapper around the
Connection
object, that intercepts calls to the Connection's "close" method.  As a
result
instead of truly closing the connection, it merely returns the
Connection to
the pool.  The proxy also adds a method to check if the connection is
still alive.

So the proxy is one way of ensuring we have a component.  It is also a
way of
returning a component automatically after a certain timeout has elapsed.
If the
component is needed again, the proxy is able to pull a new instance from
the
pool to perform the task.  However, such a use does have consequences to
the design
of your components (i.e. a component can't store state information if it
would
automatically be returned to a pool after a timeout).

One of the nice things about a proxy style component is that we can also
enable
a type of garbage collection behind the scenes.  Consider Cocoon.
Cocoon has
a hierarchy with the Cocoon object as the root container, and each
sitemap is
a container in and of itself.  Each container in the hierarchy knows
when a
request has been fulfilled--one way or another.  As a result, the
container can
clean up all the components automatically without consulting the child
components
if they are done with the handles yet or not.

Cocoon's pipeline components like the generators, serializers, et. al.
would either
need to be designed so that they are automatically returned to the
container
when they are done being used, or they would need to be changed so that
the Generator
returns an XMLSource, the Serializer returns a ContentHandler, and the
Transformer
returns an object that mixes the two.  Once the pipeline has hit the
endDocument()
SAX event, it can clean up after itself!

In order to make it easier for the component developers, we need to have
a
dynamic proxy generator (that would also help with DataSourceComponet
too...).
It would wrap the interface to enable garbage collection of components.
All a
client is responsible for is to request the component.

Anyway I am open to comments now.


--

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


---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Mime
View raw message