The work I have been doing over in Cocoon town (piece by piece) has led me to
accept certain requirements of request/response style systems.

* The system must adapt to the current environment, which may include
  parameters for a particular request or a session.

* Course grain components like Servlets handle this remarkably well because
  the request/response/session information is passed to the component with
  each request.

* Processing requirements and usage requirements are two totally different
  things.  For example, a published interface for a component only has the
  client interface in mind.  However, that is not the only picture.

* A component might change its response based on its client.  For example,
  the "cocoon:" psuedo protocol in Cocoon maps relative paths to the sitemap
  that specified the "cocoon:" URL.  Traditional container heirarchies don't
  map well to this problem.

These are challenges in many component frameworks, and they are not unique to
Avalon.  So what should we do about it?  It is not an easy question to answer.
Finding a generic solution to specific problems is kind of the holy grail that
we aspire to.  (well ok, maybe its just me ;P)

So how do we even begin to look at this problem.  The simple answer is to get
back to basics.  One of the most important design patterns we use is Multi-
Dimensional Separation of Concerns (MDSOC).  We can call it SOC for short.
It is the foundational principles that drove the development effort behind
aspect programming.

Right now, we have identified that we have an interface and a component
implementation.  That interface, which represents the code portion of the
role, specifies the _client_ concerns.  It is the interface that we expect
to use regardless of what happens behind the scenes.  We have a number of
interfaces that represent different _lifecycle_ concerns.  We have the ability
to bring a component from the initialization phase to the active phase, and
then finally to the destruction phase.  However, what we don't handle well
is changes to the environment in the active phase.

The Re* interfaces don't properly identify the concerns that happen when a
component is running.  The Recompose interface is a hack, as the lookup
interface (the ComponentManager or ServiceManager) should be able to adapt.
However, it seems to be the only solution to Cocoon's little issue with
resolving the "cocoon:" interface.  The Reconfigurable interface might be
useful in some contexts, but most cases that needed to change their
configuration at runtime handle that aspect themselves, and they only need
a way to persist the changes.  Recontextualizable also has some issues with
it as well.

The whole thing with the re* interfaces is that they allow other containers
to set those artifacts--or at least that is the only use found for them so
far.  I find that a security issue as well as a logistical one.  If we release
a component to the new ServiceManager it was given, will it really be released
properly?  We don't have that guarantee.  It may, but it may not.

I think one possibility that we haven't looked at is the fact that a component
does not necessarily have to directly implement an interface if there is a
reasonable direct translation.  For example, the two following interfaces are
for all intents and purposes similar enough:

package org.apache.avalon.test;

interface EchoServer
{
    String echo(String input);
}

package com.mybiz.foo.test;

interface PingServer
{
    String echo(String input);
}

Now, we can have a component that does what we need for the EchoServer, but our
system is looking for a PingServer.  We could easily enough proxy our EchoServer
implementation but implement the PingServer interface.  Even the method names
are the same.  Its a nice way to wrap components without manually writing the
facade.  It is also a nice way to have component implementations in another
class heirarchy proxied into the one you are working in.

What would happend if we take this simple trick and adapt it for our use?

For example, let's say we want the EchoServer above to be our client interface,
but we want the implementation to adapt to the environment.

We could provide some complex ThreadLocal management and Context manipulation,
with a little bit of voodoo mixed in for good measure.  I tend to think this
solution is a bit too complicated for someone to come in and understand it.

Now, imagine if we have a "parameterized" EchoServer that looked like this:

package org.apache.avalon.test;

interface RequestBasedEchoServer
{
    String echo(Request request, String input);
}

The client does not have the Request object, nor should we force it to.  That
type of contract is fragile.  The thing is that we could easily tool this type
of proxy.  All we are doing is adding a "Request" parameter before the rest of
the methods.  Any state information would be stored with the Request, or a
Session object accessible from the request.

So far, so good.  However, how do we resolve this Request object, or customize
it depending on where we are with the system at large?

Hopefully, experience with Servlets has taught us something.  Essentially we
have one Request object for the life of the entire request.  One session object
for the life of the entire session.  The next question is how do we know that
the Request object won't be corrupted as we are processing things.  In most
applications, this is not really much of an issue.  Most servlet developers are
pretty good about respecting the values that they store in the request/session
objects.

However, when the larger "component/container" question comes into the picture,
we very well may use third party components.  How do we ensure they don't mess
up other people's stuff?  Secondly, is it really our responsibility?  The more
I think about it, I really think that we can overthink that problem.  We can
suggest a naming scheme like Sun did for Java packages, and that would be
enough.  Any manipulations of the Request object values would have to be a
documented part of the component's contract.  In systems like Cocoon, the
sitemap root can be customized so that as the request is passed from sitemap to
sitemap, that value is set, or even a stack implementation would be used.

That would be enough for most things.  In fact, such a system would be truly
powerful.

Of course another variation of this approach is to have a SessionEnabled
interface with a setSession and an unsetSession.  The proxy would call the
set and unset methods surrounding each method call.  The whole process would
have to be synchronized, which could introduce some bottlenecks.  However, on
the plus side, we would have singleton components that adapted to what was
currently going on, making the whole concept of pooling unnecessary.

--

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



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



Reply via email to