Leo Sutic wrote:

> ShutdownRequestingContext src =
> (ShutdownRequestingContext)
> context.get(ShutdownRequestingContext.class);
>
> DirectoryContext dc =
> (DirectoryContext) context.get(DirectoryContext.class);
>
> MailForwardingContext mfc =
> (MailForwardingContext) context.get(MailForwardingContext.class);
>
> if all you know about are the "atomic" interfaces, as well as support:
>
> JAMESComponentContext jcc =
> (JAMESComponentContext) context.get(JAMESComponentContext.class);
>
> if you know about the "molecular" interface. Note that client code is
> completely isolated from whether the relationship between the context
> implementation and the interface implementation is IS-A or HAS-A.

Noel,

your approach works, but there are still a mass of casts being done
(one per get()), and the last operation (the one with JAMESComponentContext)
still requires some tricks with proxies. Furthermore, while you do
hide the IS-A/HAS-A specifics of the context, there are other ways of
doing this, and I think your approach is a clumsy way of doing it,
as it forces the user to deal with a myriad of atomic interfaces.

Issues:

JAMESCOMPONENTCONTEXT STILL REQUIRES PROXIES
--------------------------------------------

I'm assuming here that

interface JAMESComponentContext extends
MailForwardingContext,
ShutdownRequestingContext,
DirectoryContext {}

I'm also assuming that the container writer is aware of the existence of
MailForwardingContext, ShutdownRequestingContext and DirectoryContext
(as well as some other context interfaces, say, ClassLoaderContext),
but that the container writer is not aware of the existence of
JAMESComponentContext.

So if we look in the container's code, it'll look like this:

class ContextImpl implements Context,
MailForwardingContext,
ShutdownRequestingContext,
DirectoryContext,
ClassLoaderContext { (implementation) }

Now, how can you return a JAMESComponentContext? This class didn't exist
when the container was compiled, and while the ContextImpl class implements
all methods of JAMESComponentContext, you can not cast it to a
JAMESComponentContext.

In short, when I call get(JAMESComponentContext.class), how do you
provide it? You should be able to, because you do provide all operations
in the interface.




RESTATEMENT OF MY POSITION
--------------------------
(In the spirit of "if I keep repeating it, it will be true".)

:-)


The Context can be seen as a set of operations or services
that the container provides to the component. These services
may be simple getters:

File getHomeDirectory ();

No problem, manageable - its basically a accessor to a context entry.



or performance information:

long getSystemUptime ();
long getSystemIdleTicks ();

Problamatic.

The method getSystemUptime() is introducing behaviour into context which is very different from the getHomeDirectory() accessor scenario. To handle getSystemUptime() would involve mapping the invocation to a container internal service (or context entry) and invoking an operation on the service or entry. That's an order of magnitude more complex that what the Context contract requires today.

Why isn't something like this provided as a service by the container based on depedecnies declared by a component? Thats the purpose of service dependecies - to handle access to service - and what your describing looks like, smaells like, and feels like a service.

<type>
<dependencies>
<dependency optional="true">
<role>system</role>
<reference type="SystemService" version="1.3"/>
</dependency>
</dependencies>
</type>


or ways for the component to request actions of the container:

  void requestShutdown ();

 <type>
   <dependencies>
     <dependency optional="true">
       <role>parent</role>
       <reference type="ComponentHandler" version="1.1"/>
     </dependency>
   </dependencies>
 </type>

We have bunched related operations into interfaces. For example,
the requestShutdown operation is in the RequestShutdownable interface
(following the Avalon practice of appending "-able" to just about
everything here).

Now, the container will provide a set of operations, call that set P
(for Provided). The component will require a set of operations, call
that set R (for Required).

Which map to service depedency descriptors and service provision decriptors. Why do this on context when the framework has a complete and viable solution for this using the servicable/composable contract?

Obviously, if R isn't a subset of P, we're toast. The container
can't sprout new code, after all. (And to forestall the "ok so we
make it pluggable" solution: If you can make it pluggable, it isn't
part of the Context, it is then a peer service.)

OK, so the problem now is:

 1. How does the component specify R?
 2. How does the container provide R?

My solution is:

 1. The component writer creates an interface that extends those
    atomic interfaces (RequestShutdownable etc.) whose operations
    it requires. Call this interface ComponentContext.

    The component then declares that as its desired context
    interface.

    This guarantees the following:

      +  The context object passed in to the contextualize
         method can be casted to ComponentContext.

No problem here.



2. The container needs to do two things: First, verify that
it can indeed provide all the operations. Second, provide
them via the required interface.

The container must have some implementation of a Context.
Each operation is basically defined by a method signature.
Thus, it should be trivial to confirm that R is a subset of
P, or abort if it isn't.

The container can now create a dynamic proxy object that
implements the required context interface. Method calls
through the ComponentContext interface are routed as
needed to the methods of the context implementation.

Hang - on - there may be an interpritation problem here.

If your thinking about this problem from the point of view of ECM or Fortress - then you basically thinking about a solution to depedecy management that is different to the lookup operation semantics. In effect what you are describing is something similar to the current existing dependency resolution approach used in Merlin and Pheonix. I can imagine that an ECM or Fortress user would not want to "polute" existing dynamic lookup semantics with facilitiy oriented service resolution. On the other-hand, if you looking at this from the Merlin viewpoint the lokkup operation is doing exactly what you want (100%).

My suspision is that we should not be discussing Context, but instead we should discussing the *issue* concerning insufficient specifciation of the semantics of the lookup operation in the light of two relased products (ECM and Phoenix) that use the same interface with very different assumptions.

Perhaps it is appropriate to drag back the discussion concerning a ServiceLocator but updated to a new variant:

public interface ServiceLocator
{
public Object locate( String key ); // static depedency resolution
public void release( Object object );
}

public interface ServiceManager extends ServiceLocator
{
public Object lookup( String path ); // dynamic service resolver
}

What this does is seperates the two different semantic models - static service resolution based on pre-declared depedencies, from dyanamic ECM styloe service resolution. Instead of thingking about accessing and invocing services across context, do it against services. All the above does it breaks out two service resolution styles so that you easily migrate application to static service usage without breaking or complicating existing code.

Thoughts?

Cheers, Steve.




/LS


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



--

Stephen J. McConnell

OSM SARL
digital products for a global economy
mailto:[EMAIL PROTECTED]
http://www.osm.net




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

Reply via email to