Details

    • Type: New Feature New Feature
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 5.0.5
    • Fix Version/s: 5.0.6
    • Component/s: tapestry-ioc
    • Labels:
      None

      Description

      Currently in IoC, when injecting a service, you are limited to two options:

      • Inject by type, assuming exactly one match
      • Inject by explicit service id

      In a fully featured system both of these are imperfect.

      When you take into account the idea that there will be overrides for built in services, then you can't rely on their being just a single match. That's what the Alias service in tapestry-core is all about: contributing to the MasterObjectProvider to handle overrides. Unfortunately, contributions to Alias are clumsy, as they have to rely on @InjectService for disambiguation.

      Having to know the service id is a problem, since service ids are not refactoring safe: refactoring the service interface name will likely change the service id, but @InjectService annotation values will still point to the old name.

      Imagine instead a marker annotation; no attributes, attached to a type. Maybe it's attached to the service implementation class, maybe its specified to the ServiceBinder.

      When injecting, the parameter type and annotations are checked for any known marker annotation (all the marker annotation will be identified at Registry start up). The parameter type is matched against services with the marker, at must be unique for just those services.

      An idiom involving a nested annotation named "Local" is likely, thus:

      public class MyModule
      {

      @interface Local { }

      @MarkedBy(Local.class)
      public MyService buildMyService(@Local OtherService other)

      { ... }

      I'm fairly certain Guice has something quite similiar.

      The question is: can we get rid of service ids entirely?

        Activity

        Hide
        Davor Hrg added a comment -

        some thoughts....

        when we need a specific implementation of a service...

        Service binder could add a marker automaticaly,
        the marker would be impl class.
        service binder can have a method to add additional markers

        using impl class as a marker can rise issues I'm nota aware currently...

        Show
        Davor Hrg added a comment - some thoughts.... when we need a specific implementation of a service... Service binder could add a marker automaticaly, the marker would be impl class. service binder can have a method to add additional markers using impl class as a marker can rise issues I'm nota aware currently...
        Hide
        Davor Hrg added a comment -

        the above example definition for loacal annotation should be
        @Retention(RetentionPolicy.RUNTIME) @interface Local{}

        framework should warn, or even fail if @Retention(RetentionPolicy.RUNTIME)
        is not set for the marker interface.

        I've looked at guice and found following:
        markers are called binding annotations in guice,
        the service binder part is same as tapestry will probably use (different method name: markedBy)...

        bind(Service.class)
        .annotatedWith(Blue.class)
        .to(BlueService.class);

        injecting goes like this:

        @Inject
        void injectService(@Blue Service service)

        defining the "marker" interface is bit more complicated...

        /**

        • Indicates we want the blue version of a binding.
          */
          @Retention(RetentionPolicy.RUNTIME)
          @Target( {ElementType.FIELD, ElementType.PARAMETER}

          )
          @BindingAnnotation
          public @interface Blue {}

        Show
        Davor Hrg added a comment - the above example definition for loacal annotation should be @Retention(RetentionPolicy.RUNTIME) @interface Local{} framework should warn, or even fail if @Retention(RetentionPolicy.RUNTIME) is not set for the marker interface. I've looked at guice and found following: markers are called binding annotations in guice, the service binder part is same as tapestry will probably use (different method name: markedBy)... bind(Service.class) .annotatedWith(Blue.class) .to(BlueService.class); injecting goes like this: @Inject void injectService(@Blue Service service) defining the "marker" interface is bit more complicated... /** Indicates we want the blue version of a binding. */ @Retention(RetentionPolicy.RUNTIME) @Target( {ElementType.FIELD, ElementType.PARAMETER} ) @BindingAnnotation public @interface Blue {}
        Hide
        Howard M. Lewis Ship added a comment -

        Committed the code changes, but won't close the issue until the docs are updated.

        Show
        Howard M. Lewis Ship added a comment - Committed the code changes, but won't close the issue until the docs are updated.

          People

          • Assignee:
            Howard M. Lewis Ship
            Reporter:
            Howard M. Lewis Ship
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development