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]

Reply via email to