jakarta-eeannotationscdiinterceptor

How to Provide Runtime Parameters to a Custom Annotation's Interceptor in a Jakarta EE Project?


I'm working on a Jakarta EE project where I have a custom annotation and an associated interceptor. The annotation is used to execute some logic, and it works as expected as a standalone. However, I need to pass a runtime value (specifically, the result of userContext.getUserId()) to the interceptor when the annotated method is invoked.

Here's a simplified version of my setup:

@Named
@ViewScoped
public class MyClass {
    @Inject
    private UserContext userContext;
    
    @MyLog
    public void myMethod() {
        // Method logic
    }
}

The @MyLog annotation and the interceptor are defined in dependency A, which does not have access to dependency B, which provides the UserContext. MyClass however has both dependencies, A and B.

My questions are:

  1. I understand that annotations in Java can generally only accept constant values. Is there a way to pass the result of userContext.getUserId() as a parameter to the annotation at runtime?
  2. If not, what would be a recommended approach to pass or inject such runtime-dependent data into the interceptor, if possible?

I'm looking for a solution that fits well within the Jakarta EE framework. Any guidance or examples would be greatly appreciated!

I initially tried to pass the userContext.getUserId() directly to the annotation like this:

@MyLog(userContext.getUserId())
public void myMethod() {
    // Method logic
}

I was hoping this would allow the userContext.getUserId() value to be passed as an annotation value and then used by the interceptor at runtime. However, I realized that annotations in Java can only accept constant values, so this approach didn't work.


Solution

  • Following Solution worked for my problem:

    A Solution that worked for me was Implementing a ContextHolder in Dependency A.

    @Named
    @SessionScoped
    public class MyContextHolder implements Serializable {
    
        private static final long serialVersionUID = 2L;
    
        private static final ThreadLocal<String> userIdHolder = new ThreadLocal<>();
    
        public static void setUserId(final String userId) {
            userIdHolder.set(userId);
        }
    
        public static String getUserId() {
            return userIdHolder.get();
        }
    }
    

    Aditionally I created a new annotation @Contextualize in the same Project as MyClass, which uses an interceptor to set the UserId in the ContextHolder (make sure that the @Contextualize0 Annotation has a higher (lower number) priority than the @MyLog Annotation).

    public class MyClass {
        @Contextualize
        @MyLog
        public void myMethod() {
            // do Something
        }
    }