Re: Multiple lifecycles per container
Bert van Brakel <[email protected]> 28 Jul 2003 02:15:31 +1200
| Newsgroups | gmane.comp.java.plexus.devel |
|---|---|
| Organization | Tua Works Ltd |
| Message-ID | <1059274440.18780.189.camel@localhost> |
I really don't know where to start for an incremental patch. It'll basically require me to do it all over again from scratch with double the work to make the whole thing work during the increments. I basically saw how it worked at the time, then how I thought the design was heading to and what was most intuitive, did a code reshuffle, added fixes for problems as I came across solutions, then ironed out the problems during testing. I'd be willing to add extra documentation, write something up in xdoc, add a few diagrams. Just apply a patch to your working code and follow it through. You'll see its cleaner and pick it up quickly. I've added comments in the source so there shouldn't be to much confusion. I'm not that smart so I haven't done anything really tricky. Most interesting methods are ComponentRepository.lookup(..) and ComponentRepository.initialize(). I think just applying the patch and carrying on from there is the most productive route. It adds the extra features wanted and fixes a number of the issues, all for little work on your part. We could try to apply it incrementally but is that the most productive? The code base is fairly small so unintended consequences are minimal, all the current tests pass (bar the ComponentRepository.getConfiguredComponent(), but for obvious reasons), the end result is most likely going to be very similar, so why bother? If the worry is about breaking things which the tests won't pick up on, then more tests should be written. After all, that's the whole point. You could also start a new branch in cvs and have folks play with that for a while if you're really worried. Looking at the patch it seems quite complicated, however in concept it's quite simple. There's been some decoupling which should make it easier to understand. Essentially the component repository no longer handles any lifecycles for components. It's job is just to load the InstanceManagers, LifecycleHandlers, and ComponentDescriptors. It makes the LifecycleHandlers available for general use (still need work to make these immutable when passed out, don't want naughty components added/removing phases). Upon component request it looks for the instance manager handling the requested component (InstanceManager per class), if the manager isn't instantiated it does so, then calls getComponent() on the manager and passes the component back. When the component is released, it again looks for the InstanceManager and calls release(..). The changes put the onus on the InstanceManager to decide on component lifecycle handling, which also simplifies the component repository. The instanceManager can decide on whether to dispose of components immediately , pool them, or whatever. Basically the same as you had before, except it now also handles release(..). It also asks the ComponentRepository for the lifecycleHandler for the component (in the component configuration) That's the concept, stuff below is what makes it happen: ---- In the component repository the old mapping between component instance ==> ComponentHousing has been changed to component instance ==> InstanceManager to facilitate finding the InstanceManager when releasing. I've also added a lock object (lookupLock) in here to ensure that when new InstanceManagers are created that two separate threads don't create the same manager. Shouldn't drag down performance as an InstanceManager is only created once per component class. Separate object as we don't know what's locking on the ComponentRepository. Added a LoggerManager to the component repository to be used for component logging The InstanceManagers hold the ComponentHousing and track component connections, and ask the component repository for the LifecycleHandler it wishes to use (specified in the ComponentDescriptor). The instance manager is responsible for complete handling of the component lifecycle. The component repository no longer does this. While writing this I think that the ComponentHousing can be removed and just use the ComponentDescriptor and InstanceManagers directly, ditto with the ComponentManager (just common code in here. Just put it in the AbstractInstanceManager.) The LifecycleHandlerHolder performs a similar role to the ComponentDescriptor. Holding a LifecycleHandlers configuration, instance etc.. hmmm, I should of called it LifecycleHandlerDescriptor. The component repository uses this to create a new LifecycleHandler. Because the lifecycle stuff is no longer passed to the component repository by the container, the container is also affected. Just removed the lifecycle stuff. Also there was a problem with the container obtaining components on startup (load-on-start), but never releasing them. Now they are released straight away. This was I think the cause of an extra instance being created. If one wants a single instance to hang around, then just use a different InstanceManager( eg KeepAliveSingletonInstanceManager). The container only starts the component repository. ComponentHousings don't track connections, InstanceManagers do. Not even sure we should keep the ComponentManager. Just use the InstanceManagers directly. Adds an extra layer of complexity for no gain. By plugging in an InstanceManager which tracks component connections pete's problem with singletons go away. ------ Not sure really how the above can be applied incremenally, as it affects nearly everything. The stuff below possibly could. ------ Added extra logging statements at various locations to track what's going on. Hardly any previously, making it hard to find bugs. Modified LogEnablePhase to name the logger by ComponentClass. AvalonServiceSelectorTest used a deprecated method, getComponent(..), changed it to lookup(..). PlexusLoaderServlet didn't interpolate my configuration file with 'plexus.home' so I added that to the container context. SingltonInstanceManager renamed to ClassicSingletonInstanceManager, as there's a new one to keep an instance alive, which seems more like expected behaviour (like the old turbine). ClassicSingletonInstanceManager is still the default. DefaultPlexusTest has been updated with the multi thread singleton test (one instance for ever). Had to add a new ServiceG along with changes to the test configuration. Added a Tracer class as a convenience to tack stackTraces to exception messages for exceptions which don't take in a cause. I've also added a ThreadSafeMap (with TestCase, could be fleshed out), to allow multiple synchronized reads, locking writes. Only useful with lots of reads, little writes. This is used in a few locations like the Map holding the InstanceManagers in the ComponentRepository. Here InstanceManagers are looked up very often, but only created once, so a synchronized map would slow things down. Before the whole component repository was not threadSafe, as un-synchronized HashMaps were used in areas with possible concurrent reads/writes. Works on put/add/remove. Iterators require a bit more thought (possibly by using a copy of the Map). I still use arrays instead. Added a TestThreadManager, AbstractTestThread used in testing threading issues. Added a SweeperPool (with TestCase) for the PoolableInstanceManager for true pooling support. Needed to add configuration support to the InstanceManagers to allow configuration of the pool. Some more cleanup can be performed, like dropping the ComponentManager, ComponentHousing, renaming the LifeCycleHandlerHousing to LifecycleHandlerDescriptor, modify the LifecycleHandler to deal directly with the InstanceManager instead of going through the ComponentHousing.getComponentManager() to reduce coupling even further. Also added documentation changes which also made it bigger, but by that time I'd was already making changes to the workings. I think a few unintentional formatting changes were also made by my code formatter, so apologies if that's added to the patch size, though I tried to fix these again when making the patch. I know it's a big patch, bigger than I originally thought, but hey that's life. Call it punctuated evolution of plexus. You can of course not bother with it and do all that work yourself. It's yours if you want it, if not, that's fine too, thought it would be nice to share the upgrade. Let me know what you think. I'd be happy to help out but I really don't like doing things twice. The patch should be for current HEAD. Give it a whirl, you'll be glad you did :) Cheers, -bert On Sun, 2003-07-27 at 10:51, Jason van Zyl wrote: > On Fri, 2003-07-25 at 00:22, Bert van Brakel wrote: > > I've just put it into jira. Diffed off the cvs HEAD at round about 1600 > > NZ (GMT +12). > > > > I reproduce what I put in there, and attached the patch: > > ============== > > > > Well I can work with you to take pieces out of this patch but it's just > too big to integrate in one shot. > > How about we start simple with Pete's notes on the singleton problems > and getting that in? I think that would be fairly easy and get us > started.