My brain is now overloaded. On Thu, Oct 28, 2010 at 7:33 AM, Tom van Dijk <[email protected]> wrote: > Hi, > > I'm discovering some interesting implications of dynamic services. > > In hibernate-core, building dynamic services causes at least two services to > be realized. > > Let us assume that dynamic services are created based upon a contribution to > a service. > For example, dynamic services of hibernate-core are created based upon > contributions to the service HibernateMarkerConfiguration. This service is > injected into the dynamic contributor method (parameter injection) > The use of a HibernateMarkerConfiguration service could be considered > bending the semantics of a "service" a bit, since the service doesn't do > anything apart from providing the markers. > However, there really is only one way right now to contribute the markers, > and that's by contributing them to a service. > > The real "problem", however, is not that I'm using a service. > > The real problem is that in order to realize the > HibernateMarkerConfiguration service, other services have to be realized as > well, in particular the MasterObjectProvider and ServiceOverride. This means > that after the collectDynamicServices() and collectDynamicContributions() > steps, no dynamic contributions can be done to MasterObjectProvider and > ServiceOverride. Worse, if your contributions to ServiceOverride depend on > HibernateMarkerConfiguration or something like that, there is a recursion > error. Obviously, there's addInstance() to deal with this, but this will not > solve the problem that no new contributions can be done by dynamic services. > In our case it's even worse, since we have a static contribution to > ServiceOverride which depends on HibernateMarkerConfiguration to figure out > whether there are any markers. Voila, recursion. (See appendix below) > > The idea is that dynamic service contributors could use injected parameters > for their building, to increase expressivity. > > The implication of this is that a combination of modules can cause errors, > while each individual module works fine. Maybe module X depends on service S > to dynamically create classes, and module Y wants to add (dynamic) > contributions to service S. Independently, these modules can be in the > registry, but their combination will fail, because the service is realized > before the dynamic contribution is done. ((Note: in my current > implementation, this won't even be noticed by the Registry, because there is > no checking whether dynamic contributions are done to realized services)) > > Fill in for X, Hibernate-core, for Y, Hibernate, and for S the > ServiceOverride service (Where ServiceOverride elementOf dependencies(X), > due to the injection of HibernateMarkerConfiguration depending on > MasterObjectProvider to be realized which depends on ServiceOverride to be > realized) and we nearly have our case**. It would even be a problem if X and > Y are equal. > > There are several ways to cope with the issue. > > > > POSSIBLE SOLUTION 1 > A. Allow updating the configuration of services. Several possible strategies > include: rebuilding the service when the configuration is updated; calling > an updateConfiguration() method in the implementation class. Note that > updating the configuration always means adding a new configuration entry. > B. Generate an error when there is a dynamic contribution to a service that > has already been realized and that is not update-able according to A. > C. Allow injection of objects into Dynamic Service Contributors and Dynamic > Contribution Contributors. > > This does not mean every service must be modified to allow > rebuilding/updateConfiguration() calls. By default, services can be "not > update-able". Then, whenever users have clashes, we could modify services to > be update-able. For example, MasterObjectProvider and ServiceOverride could > possibly be made update-able. We need to design the syntax for this update > idea, possibly defining a new annotation to services or service > implementations ("@Updateable", with an enum Updating.PROHIBIT, > Updating.REBUILD, Updating.METHOD; in the last case exactly one public > method in the implementation must have the @ConfigurationUpdate annotation > OR be named updateConfiguration) > > > > POSSIBLE SOLUTION 2 > No injection of objects into Dynamic Service Contributors and Dynamic > Contribution Contributors. A different mechanic must be found to contribute > to dynamic services/contributions. Perhaps Dynamic Services/Contributions > could be identified using a "Dynamic id" (which can be shared by different > dynamic contributors) and which other classes can contribute to. > This will severly limit the options for dynamic service/contribution > contributions, as they may not have injected services (or something like the > ObjectLocator) as parameters. > This means no services will be realized during the dynamic part. > This will not mean that a HibernateMarkerConfiguration service will not > exist; it will still exist because there could be static services that may > want to know which database configurations there are (e.g. HibernateUtil in > my solution). > > > > With solution 1, this might mean more work and maintenance of existing > services, although good test cases for every modified service will make this > process much smoother. > With solution 2, this might severely limit the options for dynamic services. > > > The use case of our multiple database problem can be generalized to the > problem of services with multiple configurations. Essentially that's what we > have here: 6 services (hibernate-core) that have N configurations (e.g. Red > and Blue in the appendix). > > Perhaps there are other use cases that need to be considered in the context > of dynamic services/contributions. An example of this is Spring integration, > which I have not looked into yet. (One problem there is that discovery of > spring objects relies on the ServletContext object, which set in the > application globals by tapestry-core during startup... but perhaps core > could be modified to contribute the ServletContext object in some other way, > so it can be discovered by a dynamic solution - until that is solved, > tapestry-spring has to use a TapestyFilter subclass). > > > > > POSSIBLE SOLUTION 3 > > Alternatively, instead of this dynamic service jungle, we could consider > only solving the "services with multiple configurations" problem by using a > @MultipleConfigurations("MCid") annotation. > Modules could statically contribute to a MCid using some syntax. > (@ContributeMultipleConfiguration("MCid") Class[] contributeMarkers() { > return new Class[] {Red.class, Blue.class} }) > If placed on a builder, this means "for every contributed marker, generate a > service with additional marker; if no markers contributed, generate a > service without additional markers;" and the marker (or null if no markers > defined anywhere) would be injected into the build method. > If placed on a contributor, this means exactly the same. > Alternatively, contributors may also have the parameter > ("MultipleConfiguration") (with subclasses MultipleMappedConfiguration, > MultipleOrderedConfiguration....) which exposes the Class[] array of > markers. > > We would have to consider what to do with the case where no markers are > contributed to a MultipleConfiguration. In the solution I just described, it > would fall back to generating a single service. However, maybe one would > always want to create the "no markers" service, using ServiceOverride to set > the default service implementation to the "null" service.... e.g. with > multiple database, if {Red,Blue} are contributed, create Session, @Red > SessionRed and @Blue SessionBlue; if no markers are defined, use Session. > (My current implementation is: if {} is contributed, create Session; if > {A,B,C...} is contributed, create @A SessionA, @B SessionB, @C SessionC, > ...) > > > > > TLDR; > collecting dynamic services/contributions cause services to be realized, > causing problems with contributions, as well as causing recursion. > solution 1: allow services to be updated when there are new contributions > solution 2: use a different mechanism to contribute configuration to dynamic > service contributors and do not allow services to be realized during > collection > solution 3: do not allow dynamic services, instead create a syntax for the > "services with multiple configurations" concept > > > I would love feedback. Can't make all the decisions on my own, I'm too > inexperienced for that. > Also, there may be even more things I've overlooked. Please let me know if > there are more issues. > > Tom. > > > > ** I say nearly, because my current implementation has > HibernateMarkerConfiguration injected into contributeServiceOverride, which > obviously won't work. I could modify this to create a dynamiccontribution, > contributing to Service Override. In this case, the contribution will be > lost. > > > Appendix: > Registry changes by dynamics > AspectDecorator : DEFINED => VIRTUAL > HibernateMarkerConfiguration : DEFINED => REAL > MasterObjectProvider : DEFINED => REAL > ServiceOverride : DEFINED => REAL > SymbolSource : DEFINED => VIRTUAL > TypeCoercer : DEFINED => VIRTUAL > DefaultHibernateConfigurerBlue : => DEFINED > DefaultHibernateConfigurerRed : => DEFINED > HibernateEntityPackageManagerBlue : => DEFINED > HibernateEntityPackageManagerRed : => DEFINED > HibernateSessionManagerBlue : => DEFINED > HibernateSessionManagerRed : => DEFINED > HibernateSessionSourceBlue : => DEFINED > HibernateSessionSourceRed : => DEFINED > PackageNameHibernateConfigurerBlue : => DEFINED > PackageNameHibernateConfigurerRed : => DEFINED > SessionBlue : => DEFINED > SessionRed : => DEFINED > > > > > > > Op 27-10-2010 2:15, Tom van Dijk schreef: >> >> For your pleasure, I implemented the "abstract class" alternative, as a >> different branch. >> >> >> There are now a number of branches on my git >> >> trunk >> | >> fix1326 (fixes TAP5-1326) >> | >> [2 commits without a branch] (fixes TAP5-1321 and TAP5-1320) >> / \ >> / \ >> iocmod2 iocmod3 (fixes TAP5-1313, dynamic services) >> | | >> [commit] [commit] >> | | >> hibmod2 hibmod3 (fixes TAP5-48, multiple databases) >> >> the "2" branch uses annotated services >> the "3" branch uses the abstract class >> >> Maybe I should learn to "tag" instead of making a load of branches :) >> >> Tom. >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: [email protected] >> For additional commands, e-mail: [email protected] >> > > > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [email protected] > For additional commands, e-mail: [email protected] > >
-- Howard M. Lewis Ship Creator of Apache Tapestry The source for Tapestry training, mentoring and support. Contact me to learn how I can get you up and productive in Tapestry fast! (971) 678-5210 http://howardlewisship.com --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
