Now is the time to review a summary of how the Components are accessed in a system. There have been alot of ideas thrown around and clarifications. In the end, I have come to a couple conclusions:
1) There is still confusion over what constitutes a Component (I will have to write a book it seems ;P ). 2) Some people are not satisfied with the current simply role based access. We have all agreed that Poolable is an interface we want to remove. Component is one that most people want to remove--and my reservations concern people abusing the concept of a Component. The SingleThreaded and Threadsafe discussion provided a couple of solutions. There are two approaches that have been approached: 1) Merge CM and Context 2) Use Context as CM and create a new ServiceManager However, the issue I see is one of muddying contracts. What was proposed as a ServiceManager is nothing more than a ComponentManager. What was proposed as the new CM was nothing more than a Context with a release() method attached. What was not addressed to my liking was that of how a Component was to know how to manage it's threadsafety concerns (whether one instance could be shared accross all threads or it needed to have a unique instance per thread). The approach of writing a "factory" for each component is time-consuming, but it does address how to control the lifestyle policy of a Component. The major issue is one of interface. For a general purpose container to be written, the Factories would have to specify a naming convention (i.e. like URLConnectionFactory) as well as use a predefined interface. The following would be an example: interface ComponentFactory extends ObjectFactory, Disposable { setUp(Logger log, Context context, Configuration config, ComponentManager manager); } Any necessary set up to the ComponentFactory happens in the initial setUp. Even if a Component only uses one or two of the objects passed in, all must be specified to allow automatic set up of an environment based on configuration. In essence, however, we will have two layers of factories. One to create the initial object (for the pool or singleton implementations), and one for the overall component access point. If we have two implementations (one for ThreadSafe and one for SingleThreaded), all Components only have to extend one of the implementations for their ComponentFactory. This will lead to a number of classes that look like this: class DataSourceComponentFactory extends ThreadSafeComponentFactory {} class JaxpParserFactory extends SingleThreadedFactory {} While this does lead to predictable results for whatever component we are needing, it adds alot of clutter to a system. Extending a class just to change it's name seems wasteful to me. However, it does get rid of the need for SingleThreaded or ThreadSafe as marker interfaces. (This is a simple reworking of the ComponentHolder stuff in ECM). Moving on to the Context ~ ComponentManager discussion. I must admit that there are some unique possibilities with this approach that cannot be done with the current ComponentManager architecture. By employing three or four layers of Context objects, you can easily represent a system fully. Those layers are: 1) Application 2) Workspace (not all systems need this, but it is equivalent to Servlet Context) 3) Session (conversational state accross request boundaries) 4) Request (temporary state for the life of one request) However, if we are to access Components through this approach, we must consider thread interaction. The Application and Workspace layers act as a global context, and that carries the same requirements of a ComponentManager. However, the Session object has needs that are in between global context and request context. I will cover the context objects in reverse order. Most request models assume that a particular request is processed in only one thread at a time. This is true for both event drivent architectures as well as for thread per connection architectures. For each slice of time, a request is processed by only one thread at a time--even though the particular thread handling the piece of the request may change during the life of the request. We can leverage that fact to hide a token for releasing objects from a request. In essence, the Request Context would be able to have an interface like this: interface RequestContext extends Context { void releaseAll(); } Internally, the RequestContext would know that all Components requested by the Context only need to be requested once (and cache the instance for the life of the request). When releaseAll() is encountered, all Components requested so far would be released by that token. The Request Context would have to be passed with every call to a Component: cocoon.process(myRequestContext, uri); The process method would then use the myRequestContext to access what it needs: Sitemap sitemap = (Sitemap) requestContext.get(Sitemap.ROLE); sitemap.process( requestContext, uri ); Finally, the container that originally sent the request would call the releaseAll() method, and it would go through a list of all Components requested, and release them if they have not already been released. Moving to the Session Context, it follows the same principles as the request object. The only problem is that it is not outside the realm of possibility that two requests may be processed for the same session at any one time. As an example, consider a resource that takes 20 seconds to generate and an impatient web user. The web user might make the once every 2 seconds until he sees a result. That means there are 10 simultaneous requests for the same resource. We cannot rely on the Context to hide the token to release the Components retrieved for the request. We also cannot cache the instances we requested unless we keep track of the tokens for each request. One way of managing this relationship is for the Session Context to generate the RequestContexts used. The Session Context would be able to assign the token to the specific RequestContext--and the RequestContext would defer component lookup to the Session Context. When the releaseAll() is encountered on the RequestContext, any Components used by that request are released on the Session Context. Technically speaking, we can supply the same relationship to the workspace context. In essence, we can set up a WorkspaceContext like this: interface WorkspaceContext extends Context { SessionContext getSessionContext( Object sessionid ); } interface SessionContext extends Context { RequestContext getRequestContext( Object requestid ); } interface RequestContext extends Context { void releaseAll(); } If you ask about the ApplicationContext, that is taken care of by the simple Context interface. Each Component get() operation will defer to the parent context until it is resolved. It is highly unlikely that a requestContext would explicitly declare new Components to be used and would only contain certain values to control the request. For example, Each context layer is identified by an id--which can be used to resolve component mapping. For instance: Object get( Object key ) { if (key.equals(Generator.ROLE)) { return m_parent.get(key + this.get("uri")); } Object retVal m_map.get(key); if ( null == retVal ) { return m_parent.get(key); } return retVal; } While this leads to great flexibility, and the ability to alter the specific component based on the Context information, it also adds complexity that is not needed in every situation. It would be able to hide the token used for releasing Components, as well as remove the need for a separate ComponentManager and Context. It also allows the Context to handle some mapping for you. However, there are some caveats with it as well. For instance, in many systems the mapping logic is either handled by a parent, or program logic in a separate component. Whether that Component should be a ComponentManager or not can also be up for debate. The heirarchical Context approach does provide a unified view of a Context hierarchy in a system. However, it also is very server centric. We all know that Avalon is flexible enough to be used in many different contexts. Not to mention, the hierarchical Context appraoch is too complex for environments where a simple CM would do. In a web environment, the approach would work very well when all you want to do is something like this: Pipeline pipeline = (Pipeline) sitemap.process( requestContext, uri ); pipeline.sendResponse( outputStream ); requestContext.releaseAll(); The sitemap takes care of the request processing logic to build the pipeline, and you deal with the simple interface. It is all about simplifying the API for the developer. However, I believe the Context was meant to be a light-weight object, and this adds considerable bulk to the semantics of the Context. To this end, some might say that we just need JNDI--but that is just too much IMO. Perhaps the heirarchy I outlined is too domain specific. I would like your comments on it, as it does have some merits and some drawbacks. -- "They that give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety." - Benjamin Franklin -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>