avalon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Leo Sutic <leo.su...@inspireinfrastructure.com>
Subject [Avalon4:PROPOSAL] Context Consensus
Date Tue, 10 Dec 2002 23:23:52 GMT
All,

I have split the context thread in two. It is my intention to drive this 
thread - the Avalon4 branch - forward to a proposal and a vote, while the 
Avalon5 branch will probably go through many more iterations as the 
questions being dealt with are much more difficult.

                               -oOo-

                           RECAP OF POINTS

A corrected recap of the previous post follows:

DATA

There appear to be consensus on the Context being a read-only map of values 
with defined types. For example, there is consensus that a component should 
be able to request that they key "avalon:work" maps to a File specifying 
the work directory.

As for accessing those values, there are two approaches:

  1) File workDir = (File) context.get( "avalon:work" );
  2) File workDir = ((WorkDirectoryContext) context).getWorkDirectory();

There appear to be consensus that (1) is, if not preferred, then at least 
acceptable by everyone. I know of none who actively opposes such usage.

The context requirements are specified on a type level. That, each 
component class would have an associated descriptor with what context keys 
are expected, what value type it expects, etc. The above would be specified as:

     <entry key="avalon:work" type="java.io.File"/>

optionally, with name remapping:

     <entry key="workdir" intent="avalon:work" type="java.io.File"/>

The actual DTD for this, as well as the way of creating and storing such 
data varies (XDoclet generated XML, attributes embedded in the .class file, 
etc.). However, there is consensus that the data itself should be 
represented in *some* form. All proposals seem to have a similar set of 
data elements: A key, a canonical key (avalon:work), and a type specifier.

Alternatively, the entries can be defined by declaring a context interface 
(see below), for which there is metadata describing the entries in the map.


OPERATIONS

There is not consensus on whether the Context should have any active 
operations. Stephen and Berin, for example, advocates that any active 
operations should be exposed via the ServiceManager, and that the Context 
should only be used to fetch data. (Note: By active operations, I mean for 
example requestShutdown().)

Others point to the Phoenix BlockContext interface.

There is currently *not* consensus that a component should be able to 
specify any interface that it should be able to cast the context to. That 
is, for every class/interface T,

     public void contextualize (Context context) {
         T myContext = (T) context;
     }

the component should be able to declare that it expects to cast the context 
to a T. (For a less abstract example, replace T with BlockContext.)

There are two ways to provide a context instance castable to a T:

1. The first way is the one currently used in Merlin, and requires T to be 
a class (not an interface). The class is then instantiated via the 
T(Map,Context) constructor, and the instance is passed to the component. 
The Map parameter is the underlying key-value map for the context, and the 
Context parameter is the parent context.

2. The second way is not currently used, but requires T to be an interface 
(not a class). A dynamic proxy is created that exposes the T interface and 
the Context interface (required for passing). The proxy then routes method 
calls coming in through the T interface to the appropriate methods inside 
the container. (Note: This means that method calls can be routed to 
pluggable method handlers, and means that the container need not have a 
compile-time or runtime dependency on T, although it will for the usual 
reasons have a runtime dependency on it if the component using T is to be 
instantiated.)

NOTE REGARDING consensus: Darrell voiced opposition to this scheme. 
However, it seemed to me that the objection was based on the assumption 
that the container would have a dependency on T. Darrell, does your 
objection still stand?


There is consensus that a method of specifying a context interface is 
needed in order to specify the Phoenix BlockContext given its current usage 
pattern.


PORTABILITY

Regarding portability there is consensus that:

  + For data, there should be a canonical set of context keys supported by 
all containers (possibly different depending on Avalon ME, SE, EE profiles).

  + For data, there is also consensus that the more entries a component 
requires, the less likely it is to be portable, and that this is 
acceptable. That is, it is OK for a Avalon EE container to supply more keys 
that an Avalon ME container. The tradeoff is: More requirements -> less 
portability, and it is consensus that this is acceptable, given clear 
guidelines on supported keys in different containers.

  + For operations, there is consensus that operations *may* be provided 
via the context. There is also consensus that this is a much more limiting 
requirement, but given the usual tradeoffs and the presence of clear 
guidelines, it is expected to be manageable, although not recommended for 
portability reasons.

                               -oOo-

                     A BEGINNING OF A PROPOSAL


The end result of this proposal will be a replacement for the current 
Context documentation.

This part is a bit of a mixture: I have tried to only bring points where 
there is consensus into the proposal, but on one very important point I 
have taken a side - the point regarding the cast-ability of the context 
instance to a component specified interface/class. Darrell, I don't mean to 
railroad this through, and I think I give some good reasons for it below. 
If you still object, just say so and we'll see if consensus can be reached 
some other way. Finally, just because this proposal appears here it's not 
more or less official. I would, however, appreciate if we could deal with 
this by sending in corrections and objections to the text so that we have 
one proposal that everyone can see instead of several separate threads with 
proposals.


THE CAST-ABILITY OF THE CONTEXT INSTANCE

As the rest of the proposal touches on this, I thought I'd start off by 
motivating why I take a side here. The reason is that whatever our concepts 
of what a Context "really is", fact is that the Phoenix BlockContext is 
being actively used. Therefore, it and its usage pattern must be 
accomodated. Now, a container may, for example, not support any other value 
of T (as used above) except the Context interface and throw a 
ComponentSpecificContextNotSupportedException. In no way is this proposal a 
requirement for containers to include a Phoenix emulation layer by 
providing the BlockContext functionality. But if the final contract for 
Context does not even allow the current usage in Phoenix, we have 
transformed Phoenix's usage patterns from merely not being supported to 
being in violation of Framework or at the very least a highly proprietary 
extension.

I don't think it is productive to assume that all code written for Phoenix 
can be changed, no matter how small the change. Even if it would only take 
one line of code:

     Change: BlockContext bc =  (BlockContext) context;
         To: BlockContext bc =  (BlockContext) context.get 
(BlockContext.CONTEXT_KEY);

Multiply that with the number of places, and consider that the project 
where this code exist not only depends on Phoenix, but on some 30-odd other 
APIs that may change slightly, and you have a bunch of developers that do 
nothing but continuously scan the code, making millions of "simple one-line 
change[s]".

Therefore I think that if attainable, the proposal must formally make the 
Phoenix usage pattern valid. Not left as a container-specific extension, 
but formally accepted, meaning that it can't be taken away without changes 
in the stable Framework (as opposed to dropping support for a 
container-specific extension).


The purely practical aspect of this is best illustrated with a scenario: 
Phoenix CVS has burnt up, and all developers had just wiped the Phoenix 
source code from their drives (and of course no one has a backup). Thus, 
all the ex-Phoenix developers have to migrate their projects to some other 
container, and that *fast*.

Say that they decide to move to Merlin.

With the ability to specify a context interface, the BlockContext can be 
re-created, possibly by pluggable method handlers, meaning that this 
reconstruction can be done completely without affecting Merlin in any way. 
When the reconstruction is done, the code can be put in the Merlin CVS - 
either along with the phoenix client jar, or (probably) without, as a 
Phoenix compatibility pack. Even if the framework for pluggable handlers 
don't exist in Merlin, code that does add such support will be completely 
in line with Framework and thus very likely to mesh with Merlin (i.e. 
you're not adding anything that's container specific, but implementing a 
general case with basis in framework in order to plug in a special case).

The point of the above is that even if Merlin doesn't support Phoenix 
emulation, adding such code can be done in the knowledge that it isn't a 
hack, but has full support in Framework and can be done without introducing 
any coupling to Phoenix.

And that's why I'm pro-context-casting.


THE PROPOSAL ITSELF (AS IT IS NOW)

A context is defined by two sets of parameters. The first is an interface 
or a class, called T below. If an interface, it is an interface that the 
supplied context interface must be cast-able to. If it is a class, it is 
expected that the class is instantiated with the T(Map,Context) 
constructor, and that the instance is passed to the component's 
contextualize method.

NOTE: In the case where T is an interface, the container must supply an 
implementation for all methods in the interface. This may be done via a 
dynamic proxy that routes calls to appropriate handlers or by any other 
method. The set of methods that a container must support is defined by the 
standard context interfaces in Framework (currently none).

NOTE: The proposal does not cover the method the container uses to provide 
the context methods. This may be formalized at a later point but is 
currently out-of scope. The focus is on the component end of the contract.

The second set of parameters are the entries accessible via the Context.get 
method and their types. The class/interface T above may have associated 
metadata that specifies entries. These entries must be supplied by the 
container in addition to any entries the component itself requires.

Each entry requirement must specify the canonical key name, may specify a 
name that the canonical key should be remapped to, and must specify the 
expected type of the value:

For an example, where the data is specified in XML:

   <entry intent="avalon:work" type="java.io.File"/>

   <entry key="work" intent="avalon:work" type="java.io.File"/>

NOTE: The proposal does not cover the DTD, nor does it require that the 
requirements be in XML. However, it does require that the above three 
things *can* be specified.


ISSUES THAT HAVEN'T BEEN BROUGHT UP - INPUT SOUGHT

1. A list of canonical keys and their meaning can be found at:

     http://jakarta.apache.org/avalon/excalibur/info/context.html

Should these be made Framework-wide canonical keys?


2. Should the context entries include an "isOptional" attribute:

   <entry intent="avalon:work" type="java.io.File" is-optional="true"/>

    ?

/LS


--
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