Greetings.
After a couple weeks of wrestling with all three Avalon containers I've
found a couple of issues that I can't seem to get around easily.
1. Container Extensions
Sure I can extends Fortress's DefaultContainer or hack away at Merlin and Phoenix internals, but if I want to add something like JNDI, JMX, or whatever, I'd rather not have to do that. In particular, I'd like a way to write a single extension/component which works in all three containers.
that will take some time yet :D
Currently we have lifecycle extensions, but these are not yet supported by Phoenix.
patches welcome though!
What I'm looking for is a way to extend the basic services of a container without having to extend the base container class.
I want a way to dynamically add new base services like a JNDI, SOAP, JMX, etc. These services are more than just lifecycle extensions -- they're container extensions. They need access to assembly level data. As I see it, to accomplish this we would need to:
- Standardize assembly process and meta-data - Define Container Extension API (possibly based on Lifecycle Extensions) - Provide support in the main Avalon containers
Not simple, but not impossible either. This is probably something more along the lines of Spearhead, but I suppose support for it could end up in Merlin without too much trouble.
I would suggest targetting merlin for this stuff for now. Standardisation before you know your use cases is not a very good idea.
As to API, I think we keep bumping into the fact that you cannot predict what you will need from the container inside an extension. It is very difficult to predict size, scope and needs of an extension. One way to solve that is "expose everything". This implies you trust your extension and you allow it to do anything it pleases. You can make something like that more explicit by adding a few pipelines and queues of extensions in the assembly process.
With merlin, that might mean:
<block>
<engine>
<extensions>
<extension role="my-extension"/>
</extensions>
</engine><component role="my-extension" class="my.ExtensionImpl"/> </block>
---
interface Extension
{
void process( Object component, Type type, Profile profile, Appliance callbackReference, StageDescriptor stage ) throws Exception;
}
---
and somewhere inside the code that handles assembly:
LinkedList extensions = getExtensions( config );
while( extensions.hasNext() )
{
extension = extensions.next();
extension.process( obj, type, profile, getAppliance(), stage );
}---
however, figuring out a truely generic Extension interfaces has proven to be extremely difficult. The way out is to simply say "expose everything". You end up with something like:
<block>
<engine>
<interceptors>
<interceptor class="myHandler" preGoal="contextualize"/>
</interceptors>
</engine>
</block>interface Handler
{
Event handle( Event event ) throws Exception;
}class MyHandler implements Handler
{
Event handle( Event event )
throws Exception
{
Map ctx = event.getMessage();
Phase p = (Phase)ctx.get( "nextphase" ); if( p.getStage() == "contextualize" )
return handleContextualize( event );
} Event handleContextualize( Event event )
{
context.put( "secret", "this is cumbersome." );
}
}For those of you who do not like examples, some complex prose...
It is very difficult to predict in advance where the vector of change for container architecture internals lies, since many modifications to those internals are domain-specific. Hence we must strike a balance between type safety and flexibility. Maximum flexibility is gained through utilizing container internals that use "flexibility patterns" like interceptors, pipelines, events, scripting and declarative assembly.
Current containers (phoenix its kernel.xml is a good example) excel at declarative assembly while maintaining strong typing. This means that container code is manageable as long as the provided container decomposition fits the domain-specific needs. When that does not apply, any customization immediately starts feeling "hacky".
Merlin goes one step beyond in providing a more generic and more formal decomposition.
But I do not think that exposing more and more externals (as per the first example) is manageable. We will end up with a plethora of not-actually-strongly-typed extension components and no set rules for their interoperability, raising concerns about system stability.
Hence you end up at adding more flexibility features at the sacrifice of strong typing and predicatability. But my experiments so far show that adding one of these things (like interceptors to fortress using nanning) is far from trivial.
Dillemma.
2. Managing a Context
I may be mistaken, but from what I can see, between the three containers there is: - no standard set of context values - no standard context naming convention - no standard way to add things to the context without extending the base container
We've all seen the problem of relying on something like BlockContext, but BlockContext exists because it's, well, very useful. There's a need for it. But no other container supports it or has a viable alternative.
Perhaps we could: - Provide a way to include parameters/properties in the context - Provide a way to manipulate context values before general contextualization begins (this could be one of those container extensions I mentioned)
The context is great idea, but I find myself avoiding it because it seems like using Context ties me to a particular container more than I would like. Perhaps I just don't know how to use it properly, in which case, more documentation or examples, would be nice.
The root of the problem here is that no agreemnt exists between the containers about how much their contexts need to provide. Like "part of the container-component contract is that the container will provide a 'working directory' to the component on request, and that this working directory will be available through the context".
Merlin tries to solve this by allowing you to build your context from declarative xml, fortress tries to solve this by not bothering with contextualization at all and allowing you to pass in a passthrough context, and phoenix just decided on its contracts before cross-container portability became an issue.
I was also going to add something about handling resources and deployable files (think sars, jars, ears, wars, eobs, ...), but the Source Resolver package handles that fairly well. I suppose my uneasiness comes from the lack of conformity in resource structure even within the Avalon containers. Since a common server feature is loading and deploying resources like wars and sars, it seems like there would be a more complete framework (and more conformity) within Avalon for that. I'll have to ponder this one a bit more.
Peter did write a common framework for that a few times (catalina, it was called), but for reasons I can't remember that resulted in feature flexibility and ugly, hard-to-debug callstacks.
It's nontrivial.
cheers,
- Leo
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
