cdi icon indicating copy to clipboard operation
cdi copied to clipboard

Getting interceptor bindings in standard way

Open arjantijms opened this issue 3 years ago • 11 comments

As per the discussion here:

https://www.eclipse.org/lists/cdi-dev/msg00133.html

Essentially boils down to standardising the implementation specific key for this.

arjantijms avatar Feb 13 '22 16:02 arjantijms

If only we could change the InvocationContext interface. Adding a method or two to obtain interceptor binding annotations would be quite a bit better.

Ladicek avatar Feb 14 '22 07:02 Ladicek

If only we could change the InvocationContext interface.

True, we could try that - it's too late for EE 10 anyway. But I think even the original solution is fine. This isn't a feature you need very commonly anyway.

manovotn avatar Feb 14 '22 07:02 manovotn

The original solution is the key right?

I can change the invocation interface, but the Interceptors spec is technically still CDI independent.

We have at least 14 days for EE 10 btw.

arjantijms avatar Feb 14 '22 10:02 arjantijms

Technically speaking, if we standardized the key, then we could add a few methods to InvocationContext for free, because they could be default. What's more important is, if we standardize the key, where do we do that? In CDI, or in Interceptors? If in Interceptors, then EJB also needs to comply.

Ladicek avatar Feb 14 '22 10:02 Ladicek

You're right, so essentially what we have today is:

Set<Annotation> bindings = (Set<Annotation>) 
    invocationContext.getContextData().get("org.jboss.weld.interceptor.bindings");

We indeed should define it in the Interceptor spec. But nobody really wants to touch Enterprise Beans I guess. It technically doesn't have to comply, although it may. For instance, in Faces we defined some functionality that Facelets complied to but JSP just didn't. Nobody wanted to touch JSP anymore.

The method could be:

public interface InvocationContext {
    
   default Set<Annotation> getInterceptorBindings() {
       return emptySet(); // or null?
   }

arjantijms avatar Feb 14 '22 14:02 arjantijms

+1 for default Set<Annotation> getInterceptorBindings() { return emptySet(); } and the interceptors spec should state that this method returns an empty set if no interceptor bindings were used to associate the interceptors, i.e. for EJB-style association via @javax.interceptor.Interceptors.

mkouba avatar Feb 14 '22 14:02 mkouba

default Set<Annotation> getInterceptorBindings()

That sounds good. I am also +1 for empty set (rather than null) if no bindings are used as that will naturally fit into EJB binding style where an empty set is a correct return value.

manovotn avatar Feb 14 '22 14:02 manovotn

My understanding is that EJB also has to implement the full Interceptors specification, just like CDI, so even though the idiomatic way of associating interceptor with components in CDI is through interceptor binding annotations and in EJB through @Interceptors, they both have to support both...

That said, I'd be fine with adding something like the following to InvocationContext:

/**
 * Returns the set of interceptor binding annotations used to associate interceptors with the target component.
 * Returns an empty set if all interceptors are associated with the target component using the {@link Interceptors @Interceptors} annotation.
 * <p>
 * No requirements exist on mutability or shareability of the returned set.
 * <p>
 * This method is not required to be implemented by EJB containers.
 * It must be implemented by CDI containers.
 *
 * @return the set of interceptor bindings, never {@code null}
 */
default Set<Annotation> getInterceptorBindings() {
    return Collections.emptySet();
}

/**
 * Returns the interceptor binding annotation of given type used to associate interceptors with the target component.
 * Returns {@code null} if the {@link #getInterceptorBindings() full set} of interceptor bindings does not contain an annotation of given type,
 * or if all interceptors are associated with the target component using the {@link Interceptors @Interceptors} annotation.
 * <p>
 * In case of repeatable interceptor bindings, {@link #getInterceptorBindings(Class)} should be used.
 * <p>
 * This method is not required to be implemented by EJB containers.
 * It must be implemented by CDI containers.
 *
 * @param annotationType type of the interceptor binding annotation, must not be {@code null}
 * @return the interceptor binding annotation of given type, may be {@code null}
 */
default <T extends Annotation> T getInterceptorBinding(Class<T> annotationType) {
    for (Annotation interceptorBinding : getInterceptorBindings()) {
        if (interceptorBinding.annotationType().equals(annotationType)) {
            return (T) annotation;
        }
    }
    return null;
}

/**
 * Returns the set of interceptor binding annotations of given type used to associate interceptors with the target component.
 * The result is typically a singleton set, unless repeatable interceptor bindings are used.
 * Returns an empty set if all interceptors are associated with the target component using the {@link Interceptors @Interceptors} annotation.
 * <p>
 * No requirements exist on mutability or shareability of the returned set.
 * <p>
 * This method is not required to be implemented by EJB containers.
 * It must be implemented by CDI containers.
 *
 * @param annotationType type of the interceptor binding annotation, must not be {@code null}
 * @return the set of interceptor bindings of given type, never {@code null}
 */
default <T extends Annotation> Set<T> getInterceptorBindings(Class<T> annotationType) {
    Set<T> result = new HashSet<>();
    for (Annotation interceptorBinding : getInterceptorBindings()) {
        if (interceptorBinding.annotationType().equals(annotationType)) {
            result.add((T) interceptorBinding);
        }
    }
    return result;
}

I understand that the Interceptors spec ignores the existence of repeatable annotations (because they didn't exist at that time), so I'd be fine with dropping the last method (and the paragraph mentioning repeatable interceptor bindings in the javadoc of the 2nd). The 2nd seems pretty useful to me.

Ladicek avatar Feb 14 '22 15:02 Ladicek

My understanding is that EJB also has to implement the full Interceptors specification, just like CDI, so even though the idiomatic way of associating interceptor with components in CDI is through interceptor binding annotations and in EJB through @Interceptors, they both have to support both...

Well, the thing is that EJB session beans are considered CDI beans anyway. I don't think the EJB impls implement CDI-style interceptors by themselves, instead they delegate to the CDI container.

This method is not required to be implemented by EJB containers. It must be implemented by CDI containers.

I would not include these sentences at all.

No requirements exist on mutability or shareability of the returned set

Hm, I'd prefer to say that the returned set is immutable. This wording does not help anyone. A client should not modify the set and the CDI implementor should either return a new set or make it immutable (to protect users from shooting in their own leg ;-).

mkouba avatar Feb 15 '22 07:02 mkouba

Hmm, assuming that

The use of interceptors defined by means of the Interceptors annotation is required to be supported for Jakarta Enterprise Beans and Managed Bean components, including in the absence of CDI. When CDI is enabled, the use of interceptors defined both by means of interceptor binding annotations and by means of the Interceptors annotation is required to be supported for component classes that support injection

-- Jakarta Interceptors, chapter 1.3

actually means that interceptor bindings are always handled by CDI, then indeed this provision

This method is not required to be implemented by EJB containers. It must be implemented by CDI containers.

is unnecessary.

When it comes to mutability etc., I'd be fine with requiring the resulting sets to be immutable, for sure.

Ladicek avatar Feb 15 '22 08:02 Ladicek

When it comes to mutability etc., I'd be fine with requiring the resulting sets to be immutable, for sure.

Okay, so I think that for the next release cycle (post EE 10), we have our work cut out then ;)

arjantijms avatar Feb 23 '22 14:02 arjantijms

It would be nice to see this added in Jakarta EE 11. We ran into some difficulty where we are needing to rely on the WELD-specific approach in our implementation of the Transactional interceptor to properly detect the information configured on the interceptor binding at class level.

njr-11 avatar Feb 28 '23 14:02 njr-11

Yes, I think this is something we want to get in, but ideally via interceptors spec (change to InvocationContext). FTR, today's call should be focused on going through issues listed for future version of CDI and curating them in terms of which issues are still relevant and doable for next version.

manovotn avatar Feb 28 '23 14:02 manovotn

Note to self: if we go forward with this, need to add tests to the CDI TCK for this.

Ladicek avatar Jun 20 '23 14:06 Ladicek

Submitted https://github.com/jakartaee/interceptors/pull/99 for this.

Ladicek avatar Jun 20 '23 14:06 Ladicek

The linked interceptors PR was merged, I think we can close this and open a tracking issue for TCK coverage. Agreed @Ladicek?

EDIT: Mea culpa, we already have the PR! See https://github.com/jakartaee/cdi-tck/pull/479

manovotn avatar Sep 20 '23 12:09 manovotn

This is already resolved in the interceptors spec and TCK coverage has been merged as well; closing this issue.

manovotn avatar Nov 01 '23 14:11 manovotn