Uploaded image for project: 'CDI Specification Issues'
  1. CDI Specification Issues
  2. CDI-227

BeanManager#resolve() is underspecified for corner cases

      The CDI api's are mainly used to develop (portable) extensions. In practice, we see that those so called portable extensions are not portable at all due to underspecified api's.
      For example BeanManager.resolve(set) doesn't specify how to deal with an empty set or null. This leads to incompatible implementations and unportable extensions. See for example https://issues.apache.org/jira/browse/OWB-625 (Unfortunately Websphere 8 includes openwebbeans < 1.1.3, making Seam3 not out-of-the-box usable). Currently most implementations (weld, owb and candi) return null for an empty set. OWB returns null when null is passed, others will throw an exception.
      To create extensions that are truly portable, we should specify all corner cases (what about BeanManager.getBean((String)null)?). In theory developers should not depend on undefined behavior, but in practice we all do (Seam3 is full of examples).
      Ideally, we should go over the complete public API and specify what should happen with these corner cases: null-values, empty collections, ...
      Just adding that an IllegalArgumentException should be thrown in case of null for example would suffice.
      These things are very easy to incorporate in the TCK and would contribute a lot to the success of portable extensions

            [CDI-227] BeanManager#resolve() is underspecified for corner cases

            Closing all resolved issues in CDI 1.x

            Antoine Sabot-Durand (Inactive) added a comment - Closing all resolved issues in CDI 1.x

            Done, thanks

            Pete Muir (Inactive) added a comment - Done, thanks

            BeanManager.resolve() javadoc still contains reference to module resolution:

            @returns the resolved bean, or null if no bean could be resolved, or null if null or an empty set is passed

            should be:

            @returns the resolved bean, or null if null or an empty set is passed

            Martin Kouba added a comment - BeanManager.resolve() javadoc still contains reference to module resolution: @returns the resolved bean, or null if no bean could be resolved, or null if null or an empty set is passed should be: @returns the resolved bean, or null if null or an empty set is passed

            Merged

            Pete Muir (Inactive) added a comment - Merged

            I've removed the bullet about not being available in the module. The reference in the section is to "ambiguous and unsatisfied dependencies", which isn't to do with the module resolution, which happens in the previous section. As Jozef points out, getBeans() is responsible for sorting this bit out. This is backed up by the javadoc for resolve

            Apply the ambiguous dependency resolution rules to a set of

            Unknown macro: {@linkplain Bean beans}

            .

            Again only discussing ambiguous dependencies, not module resolution.

            Pete Muir (Inactive) added a comment - I've removed the bullet about not being available in the module. The reference in the section is to "ambiguous and unsatisfied dependencies", which isn't to do with the module resolution, which happens in the previous section. As Jozef points out, getBeans() is responsible for sorting this bit out. This is backed up by the javadoc for resolve Apply the ambiguous dependency resolution rules to a set of Unknown macro: {@linkplain Bean beans} . Again only discussing ambiguous dependencies, not module resolution.

            Jozef Hartinger added a comment - - edited

            I think that BeanManager.resolve() was intended to perform ambiguous resolution on a set of enabled beans that are available for injection in a given module (as returned by BeanManager.getBeans()). I think that the spec should declare this precondition on the "beans" parameters. If this precondition is met there is no way BeanManager.resolve() could ever return null for a non-null set of beans.

            Jozef Hartinger added a comment - - edited I think that BeanManager.resolve() was intended to perform ambiguous resolution on a set of enabled beans that are available for injection in a given module (as returned by BeanManager.getBeans()). I think that the spec should declare this precondition on the "beans" parameters. If this precondition is met there is no way BeanManager.resolve() could ever return null for a non-null set of beans.

            Mark, could you specify the scenario in which Weld does not follow @Alternative rules?

            Jozef Hartinger added a comment - Mark, could you specify the scenario in which Weld does not follow @Alternative rules?

            we should discuss that in the EG meeting. While thinking about it I came to the conclusion that this never worked for BDAs. Reason is that neither BM#getBeans() nor BM#resolve() has an InjectionPoint parameter. But according to the (broken) BDA definition the @Alternative must be taken into account only for injection points which are in a JAR where the <alternative> is enabled in beans.xml. Thats sick!

            Mark Struberg (Inactive) added a comment - we should discuss that in the EG meeting. While thinking about it I came to the conclusion that this never worked for BDAs. Reason is that neither BM#getBeans() nor BM#resolve() has an InjectionPoint parameter. But according to the (broken) BDA definition the @Alternative must be taken into account only for injection points which are in a JAR where the <alternative> is enabled in beans.xml. Thats sick!

            I believe this is covered in "11.3.5. Obtaining a Bean by type":

            The method BeanManager.getBeans() returns the set of beans which have the given required type and qualifiers and are
            available for injection in the module or library containing the class into which the BeanManager was injected...

            Otherwise the container would have to check all the passed beans whether they're are available for injection... which would make sense only if BeanManager.resolve() is intended to be used independently of getBeans() (e.g. with any custom set of beans).

            Martin Kouba added a comment - I believe this is covered in "11.3.5. Obtaining a Bean by type": The method BeanManager.getBeans() returns the set of beans which have the given required type and qualifiers and are available for injection in the module or library containing the class into which the BeanManager was injected... Otherwise the container would have to check all the passed beans whether they're are available for injection... which would make sense only if BeanManager.resolve() is intended to be used independently of getBeans() (e.g. with any custom set of beans).

            Martin, please read "5.1 Modularity"

            In CDI-1.0 an @Alternative has to be enabled for a certain jar. If strictly interpreted was not available in another jar. That's of course not user friendly, so this was not implemented by many containers that way. Early Weld containers had it that strictly but even Weld doesn't follow this spec rule anymore. But it is certainly defined that way in CDI-1.0.

            But even for CDI-1.1 where we have global enablement (see CDI-18) this bullet is not useless imo.

            Imagine you have an EAR with 2 WebApps. An @Alternative x2 for interface X (default implementation x1)is defined in webappA but not in webappB. Thus the phrase 'is available for injection in the module'.

            Or do you think this is covered elsewhere already?

            Mark Struberg (Inactive) added a comment - Martin, please read "5.1 Modularity" In CDI-1 .0 an @Alternative has to be enabled for a certain jar. If strictly interpreted was not available in another jar. That's of course not user friendly, so this was not implemented by many containers that way. Early Weld containers had it that strictly but even Weld doesn't follow this spec rule anymore. But it is certainly defined that way in CDI-1 .0. But even for CDI-1 .1 where we have global enablement (see CDI-18 ) this bullet is not useless imo. Imagine you have an EAR with 2 WebApps. An @Alternative x2 for interface X (default implementation x1)is defined in webappA but not in webappB. Thus the phrase 'is available for injection in the module'. Or do you think this is covered elsewhere already?

            Martin Kouba added a comment - - edited

            Hm, still don't understand. Is the mentioned edge case documented somewhere (issue/description/discussion)?

            Martin Kouba added a comment - - edited Hm, still don't understand. Is the mentioned edge case documented somewhere (issue/description/discussion)?

            Right, I was addressing the edge case described by Mark.

            Pete Muir (Inactive) added a comment - Right, I was addressing the edge case described by Mark.

            That might have to do with the CDI-1.0 Alternatives behaviour. In CDI-1.0 they are only available in the jar where the <alternatives> got defined in beans.xml according to the spec. Gladly this is not implemented that way by most containers.

            Mark Struberg (Inactive) added a comment - That might have to do with the CDI-1 .0 Alternatives behaviour. In CDI-1 .0 they are only available in the jar where the <alternatives> got defined in beans.xml according to the spec. Gladly this is not implemented that way by most containers.

            I don't get the last bullet in "11.3.8. Resolving an ambiguous dependency":

            • no bean is available for injection in the module (as defined in Section 5.1, “Modularity”)

            I think BeanManager.resolve() should only apply ambiguous dependency resolution rules. So the only possibilities are:

            1. null is passed => return null
            2. no beans are passed (empty list) => return null
            3. non-empty set of beans is passed:
              1. set.size() == 1 => the bean is returned
              2. set.size() > 1 ...an ambiguous dependency exists:
                1. is not resolvable => AmbiguousResolutionException is thrown
                2. is resolvable => the bean is returned

            However there might be some corner case I don't see...

            Martin Kouba added a comment - I don't get the last bullet in "11.3.8. Resolving an ambiguous dependency" : no bean is available for injection in the module (as defined in Section 5.1, “Modularity”) I think BeanManager.resolve() should only apply ambiguous dependency resolution rules. So the only possibilities are: null is passed => return null no beans are passed (empty list) => return null non-empty set of beans is passed: set.size() == 1 => the bean is returned set.size() > 1 ...an ambiguous dependency exists: is not resolvable => AmbiguousResolutionException is thrown is resolvable => the bean is returned However there might be some corner case I don't see...

            Merged.

            Pete Muir (Inactive) added a comment - Merged.

            please apply.

            Mark Struberg (Inactive) added a comment - please apply.

            Mark Struberg (Inactive) added a comment - https://github.com/jboss/cdi/pull/58

            Mark Struberg (Inactive) added a comment - - edited

            There are actually 2 corner cases we need to define:

            • We shall define that BeanManager#resovle(Set<Bean> beans) returns <code>null</code> if the 'beans' parameter is null or an empty Set
            • We shall define that BeanManager#resovle(Set<Bean> beans) returns <code>null</code> if no active bean got found.

            Mark Struberg (Inactive) added a comment - - edited There are actually 2 corner cases we need to define: We shall define that BeanManager#resovle(Set<Bean> beans) returns <code>null</code> if the 'beans' parameter is null or an empty Set We shall define that BeanManager#resovle(Set<Bean> beans) returns <code>null</code> if no active bean got found.

              pmuiratbleepbleep Pete Muir (Inactive)
              guy.veraghtert Guy Veraghtert (Inactive)
              Votes:
              1 Vote for this issue
              Watchers:
              5 Start watching this issue

                Created:
                Updated:
                Resolved: