users@javaee-spec.java.net

[javaee-spec users] [jsr366-experts] Re: Compatibility Problems with MR Resource Annotation Widening

From: Bill Shannon <bill.shannon_at_oracle.com>
Date: Tue, 24 Feb 2015 16:07:25 -0800

In thinking about this a bit more, you bring up some good points.

Part of what started us on this path of clarifying the expected behavior
is that we discovered some bugs in this area in the RI, or at least, behavior
that didn't match what we expected. That took us down some twisty paths
that may not have ended up in the right place.

Considering some of our history, it may be that some of this behavior is
something we can't change. For example, in Java EE 5 we didn't have JNDI
namespaces, but we did allow EJBs to be created by subclassing other
classes that might have resource injection annotations. That seems to
mean that the resource references have to be defined in the namespace
of every component that subclasses the base class.

What we were trying to define is a simple processing model for annotations
that would allow all processing to be done in a single pass over the classes.

We expected every one of these resource annotations, on any class in the
application, to define a resource reference entry in some JNDI namespace.
The namespaces available to a given annotation on a given class were a
function of where in the application the class was packaged. The deployment
process that discovers these annotations could add the required information
to the appropriate JNDI namespace without any information about how the
class was used. And it could reject applications that attempted to
declare resource references in namespaces that the class didn't have
access to.

What we're discovering is that this is too simplistic. We at least need
to handle the case where a component class subclasses some other class.
In this case, the base class may not have a component namespace of its
own, but it needs to be able to define resources in the namespace of the
component that subclasses it. Unfortunately, this means you need to know
what all the "classes supporting injection" are, and you need to know the
full class hierarchy for each of them, so that you can ensure the resource
references from the superclasses appear in the component namespaces of all
the component subclasses. Since "classes supporting injection" includes CDI
managed beans, I don't know how you can know that at deployment time.

It seems that we have to handle at least some cases of superclasses defining
resources in the same namespace as the subclass. Sadly, you may be right that
we have to handle that even in the case where the superclass is in a library.


Let's look at the general algorithm for initializing the JNDI namespaces.
(We'll ignore deployment descriptor overriding for now. We'll also
ignore application clients for now.)

1. Find all the classes in each war file.

2. Find all the superclasses of each class from table EE-5.1 in each
    war file.

3. For all these classes, add any resource references or resource
    definitions in the java:comp namespace to the corresponding
    war component's JNDI namespace.

4. For all these classes, add any resource references or resource
    definitions in the java:module namespace to the corresponding
    war component's JNDI namespace.

5. Find all the classes in each EJB jar file that define an EJB.

6. Find all the superclasses of each of these classes.

7. For all these classes, add any resource references or resource
    definitions in the java:comp namespace to the corresponding EJB
    component's JNDI namespace.

8. For all these classes, add any resource references or resource
    definitions in the java:module namespace to the corresponding EJB
    jar's module JNDI namespace.

9. Find all the classes in each EJB jar file.

10. For all these classes, add any resource references or resource
    definitions in the java:module namespace to the corresponding EJB
    jar's module JNDI namespace.

11. For all classes in the application package, add any resource
    references or resource definitions in the java:app namespace to the
    application's JNDI namespace.

12. For all classes in the application package, add any resource
    references or resource definitions in the java:global namespace to
    the global JNDI namespace.


A difficulty with the algorithm above is that table EE-5.1 includes
CDI managed beans. Without initializing the CDI runtime, it's impossible
to know what all the CDI managed beans are. In steps 2 and 9, we could
just consider every class to be a potential CDI managed bean, adding or
changing these steps:

2. Find every superclass of these classes.

9a. Find every superclass of these classes.


Oh, but we forgot about interceptors, which have their own special rules.
Add these steps:

2a. Find all the interceptors for each of these classes.

2b. Find all the superclasses of all these interceptor classes.

5a. Find all the interceptors for each of these classes.

5b. Find all the superclasses of all these interceptor classes.

It's not clear from the Interceptors spec whether these special rules
for interceptors apply to only those interceptors declared using
@Interceptors, or also to those declared using interceptor bindings.
Again, the latter are essentially impossible to determine without
initializing CDI, so we'll assume only the former was intended for
now. Since the genesis of this rule was from the EJB spec, we could
further limit this to @Interceptors applied to EJBs if desired, which
would remove steps 2a and 2b.


What's missing from the algorithm is any attempt to detect the error
where a class declares a resource in a namespace that's unavailable to
the class. To handle that, we add these steps:

13. If any class in an ejb jar file and not in the set from step 6
    declares a resource in the java:comp namespace, it's an error.

14. If any class in a library and not in the sets from steps 2 and 6
    declares a resource in the java:comp or java:module namespace, it's
    an error.

15. If any class in a rar module and not in the sets from steps 2 and 6
    declares a resource in any java: namespace, it's an error.

Ideally these would be deployment errors since it's better to detect
application errors early, but clearly that comes at some cost.

Does this algorithm better match what you're expecting, and hopefully
implementing?



Jason Greene wrote on 02/23/15 11:37:
> Sorry for the delayed reply, response inline.
>
>> On Feb 20, 2015, at 5:43 PM, Bill Shannon <bill.shannon_at_oracle.com> wrote:
>>
>
> -snip-
>
>>
>>> Example 2:
>>>
>>> Base.class (not a component) in ejb1.jar, @Resource(name = “myresource”)
>>> A.class (an EJB) extends Base in ejb1.jar
>>>
>>> Before MR we have one binding:
>>> A has a comp entry for “myresource”
>>>
>>> After MR we have two? bindings:
>>> A has a comp entry for “myresource”
>>> Base creates java:module/env/myresource?
>>
>> No. Again, it's still an error.
>
> (As a slight tangent, I just tried this on the RI and it works as I would expect (the behavior that is pre-[MR proposal]), Base.class uses the comp namespace of A.class)
>
> But, back to my point; If the MR proposal is supposed to trigger an error, then that would be even less intuitive result, since this new non-component class behavior would be the only scenario that doesn’t support unqualified name attribute values on @Resource.
>
> In all cases things get really bizarre though with the MR proposal, since simply moving an annotation on the same class changes behavior.
>
> Since JSR 250 says:
> "Even though this annotation is not marked Inherited, if used all superclasses MUST be examined to discover all uses of this annotation. All such annotation instances specify resources that are needed by the application. Note that this annotation may appear on private fields and methods of the superclasses. Injection of the declared resources needs to happen in these cases as well, even if a method with such an annotation is overridden by a subclass.”
>
> (and when combined with the EE language previously quoted) We know that the following on Base should reference A’s comp:
>
> @Resource(name=“myresource”)
> protected DataSource ds;
>
> However, if I simply move that annotation to the top of class (and add the type value of course), there is now an error.
>
>>
>
> -snip-
>
>> Once you start controlling the namespace of resources based on where
>> the class is *used* instead of *defined*, it gets very complicated.
>> Yes, you can make up some simple cases that might work reasonably,
>> but when you examine all the possible cases it's not at all clear
>> how it should work.
>
> This is already the case today though. The inheritance model for EE injection annotations is a form of use, since components ultimately define the meaning of the values on their supertypes.
>
> --
> Jason T. Greene
> WildFly Lead / JBoss EAP Platform Architect
> JBoss, a division of Red Hat
>