Return-Path: Delivered-To: apmail-jakarta-avalon-dev-archive@apache.org Received: (qmail 78770 invoked from network); 24 Nov 2002 00:15:59 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 24 Nov 2002 00:15:59 -0000 Received: (qmail 6292 invoked by uid 97); 24 Nov 2002 00:17:05 -0000 Delivered-To: qmlist-jakarta-archive-avalon-dev@jakarta.apache.org Received: (qmail 6228 invoked by uid 97); 24 Nov 2002 00:17:04 -0000 Mailing-List: contact avalon-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Avalon Developers List" Reply-To: "Avalon Developers List" Delivered-To: mailing list avalon-dev@jakarta.apache.org Received: (qmail 6216 invoked by uid 98); 24 Nov 2002 00:17:04 -0000 X-Antivirus: nagoya (v4218 created Aug 14 2002) X-Authentication-Warning: lsd.student.utwente.nl: lsimons set sender to leosimons@apache.org using -f Subject: context usage patterns From: Leo Simons To: Avalon Development Content-Type: text/plain Content-Transfer-Encoding: 7bit X-Mailer: Ximian Evolution 1.0.8 (1.0.8-10) Date: 24 Nov 2002 01:15:44 +0100 Message-Id: <1038096944.20081.84.camel@lsd.student.utwente.nl> Mime-Version: 1.0 X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N Hi gang, (new subject line, same thread) there's a few different approaches regarding component architecture decomposition. Recently we've been basically re-doing old discussion on this, which is good as I'm having fresh thoughts about how to explain what I think is best. Andy, if you're reading, here's example-based explanation :D Several approaches to using the avalon Context: 1) published keys, cast on lookup ================================= sample code ----------- public final class MyCommonContextKeys { /** java.io.File where we can store permanent data */ public static final String HOME_DIRECTORY_FILE = "avalon:home-directory"; } public class MyComponentImpl implements Contextualizable { contextualize( Context c ) throws ContextException { // if a component were able to specify by contract what it // will look up and what it will cast that to, the try/catch // is not necessary. try { final File homedir = (File)c.lookup( MyCommonContextKeys.HOME_DIRECTORY_FILE ); } catch( Exception e ) { throw new ContextException("Where's my home dir????!?"); } } } Requirements ----------- - Well-published and agreed upon context key definitions - either the component developer must do one of: o specify in some way what a component requires from the context (ie requires meta model, value of meta model is biggest when shared across the board). This becomes: // ... contextualize( Context c ) throws ContextException { final File homedir = (File)c.lookup( MyCommonContextKeys.HOME_DIRECTORY_FILE ); } // ... MyComponentImpl.profile example ----------------------------- o use lots of try/catch (and people reusing the component must then figure out what goes wrong from log messages); this becomes: non-supporting-container.log ---------------------------- #... [ERROR][MyComponent] 12-01-02 14:34 contextualize() threw an Exception: "Where's my home dir????!?" or the container developers must agree on specifying a contract stating what a container should provide in the context (limiting reuse and grinding down container evolution); this becomes: class Context { File getHome(); File getWork(); Blaat getSuch(); } MyMycroContainer implements CrappyAndTooHeavyForUse {} 2) extension of Context class ============================= sample code ----------- public class MyExtendedContext { public File getHome() { // some kind of implementation needs to go here } } public class MyComponentImpl implements Contextualizable { contextualize( Context c ) throws ContextException { if(! c instanceof MyExtendedContext ) throw new ContextException("I need a MyExtendedContext!!!"); final MyExtendedContext mec = (MyExtendedContext)c; final File homedir = mec.getHome(); } } Requirements ------------ either - all containers use the same extensions (which again leads to MyMycroContainer implements CrappyAndTooHeavyForUse {}) or - component developers write different versions of their components for different containers, which is a maintenance problem: class MyBasicComponent { // blaat } class MyComponentForContainerA extends MyBasicComponent { // different behaviour based on different options in the context } class MyComponentForContainerB extends MyBasicComponent { // different behaviour based on different options in the context } // ... it will always result in non-supporting-container.log ---------------------------- #... [ERROR][MyComponent] 12-01-02 14:34 contextualize() threw an Exception: "I need a MyExtendedContext!!!" regardless of the option chosen. It also means work for each and every container developer. 3) embedding of extended context class ====================================== Very similar to 2) except it also needs a context key registry. sample code ----------- public final class MyCommonContextKeys { /** to get at MyExtendedContext */ public static final String MY_EXTENDED_CONTEXT = "extension:my-context"; } public class MyExtendedContext { public File getHome() { // some kind of implementation needs to go here } } public class MyComponentImpl implements Contextualizable { contextualize( Context c ) throws ContextException { // if a component were able to specify by contract what it // will look up and what it will cast that to, the try/catch // is not necessary. try { final MyExtendedContext mec = (MyExtendedContext)c.lookup( MyCommonContextKeys.MY_EXTENDED_CONTEXT ); final File homedir = mec.getHome(); catch( Exception e ) { throw new ContextException( "Where's MyExtendedContext????!?" ); } } } Requirements ------------ - Well-published and agreed upon context key definitions and either - all containers use the same hierarchy of context objects or - component developers write different versions of their components for different containers My Take ======= The first option (which is also the one currently advocated by the meta/info model stuff and also by current practices if you ask me) is clearly the winner, provided we get to a common meta model. ATM we don't have a common meta model, rather de facto and undocumented ones for each container (which was not a problem back when ECM didn't do any of this and phoenix was the only player, but is now and will become in the future), meaning component developers either need to 'develop defensively' (with try/catch) or decrease the robustness of the component. Following this approach also requires container developers specify clearly which context entries they can and cannot support (something like my-container.log ---------------- [container][setup] VERIFICATION WARNING: the profile for MyComponentImpl specifies it depends on a java.io.File in the context for lookup key "avalon:home-directory" but this container doesn't know about the contract surrounding this context enty. [container][MyComponentImpl] CONTEXTUALIZATION WARNING: MyComponentImpl is looking up "avalon:unspecified-attribute" but this is an undeclared dependency. Do you need to modify MyComponentImpl.profile? ---------------- Responsibility for a lot of this is then in the container, which makes life easier on users. Perhaps combined with a QDox-like setup (for the lazy but disciplined user) this is way better than the others. It is I think also the most performant and it results in the cleanest code. Finally, I think the first approach is also the quickest one for server apps as all the work (parsing and verifying context criteria and making sure they can be satisfied) occurs on startup, or even potentially right after compilation. Services don't go into the Context ---------------------------------- This is something very important to stress. Services should not go into the context. If it is something a component acts upon actively and contains some business logic for, it probably isn't a component and can go into the context. If not, it is probably a service which you should handle through ServiceManager. example: a home directory where a component is allowed to interact with the filesystem is something to put into the context, whereas a storage service with which a component can interact to store data is not. I tend to try to use context as little as possible (in the above example, you'll see me always using the storage service rather than the context entry). While usually easy in the short run and very useful for quickly avalonizing existing apps, in the long run using context a lot decrease application maintainability. This is a lesson learned from working with Servlets (among other things). Generally, using the request and response stuff in the servlet context works really well (as you know it is there per the servlet spec). Using container-specific materials (ie stuff that WebLogic might put into a servlet context but that ServletExec does not) is a road to disaster as someone will find out when they have to move over that application you wrote two years ago to the new server farm and the only exception he gets is IllegalArgumentException :D. The balance between all this is often difficult to find ;) Properties and configuration don't go into the Context ------------------------------------------------------ If it is just as easy to use properties, parameters or configuration objects instead of the context (ie if you have data naturally expressed in character form), choose that option anytime. It's a lot clearer and generally less error-prone. okay, sorry for the length of the e-mail, but I don't want Steve to feel too alone down at the bottom of lengthy talks :D g'night all, - Leo -- To unsubscribe, e-mail: For additional commands, e-mail: