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:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
- Re: [Avalon4:PROPOSAL] Context Consensus Leo Sutic
- Re: [Avalon4:PROPOSAL] Context Consensus Leo Simons
- RE: [Avalon4:PROPOSAL] Context Consensus Leo Sutic
- RE: [Avalon4:PROPOSAL] Context Consensus Leo Simons
- RE: [Avalon4:PROPOSAL] Context Consens... Leo Sutic
- RE: [Avalon4:PROPOSAL] Context Co... Berin Loritsch
- RE: [Avalon4:PROPOSAL] Contex... Leo Sutic
- Re: [Avalon4:PROPOSAL] Contex... Nicola Ken Barozzi
- RE: [Avalon4:PROPOSAL] Co... Leo Sutic
- Re: [Avalon4:PROPOSAL] Context Consensus Paul Hammant
- Re: [Avalon4:PROPOSAL] Context Consensus Leo Simons