jakarta-eetimerejbinterceptorstateless-session-bean

How to implement an interceptor in a timer in jakarta EE9


I want to have a Stateless session bean in JAKARTA EE9 where I print "hello" on the console every 10 seconds. This is achieved by the following code.

@jakarta.ejb.Stateless(name = "MyTimerEJB")
public class MyTimerBean {
    @Schedule(hour ="*", minute = "*", second = "*/10")
    public void printTime() {
        System.out.println("good day");
    }
}

But now, I also want to add an interceptor that intercepts this on saturdays and sundays and then prints "good weekend". I've tried a bunch of things, but nothing seems to be working. The documentation is also a little bit limited unfortunately.


Solution

  • If you check out the EJB specs, section 7.4, you will find about the AroundTimeout annotation.

    In short, all you need is a normal, enabled interceptor for the bean you are interested in, with a method annotated with @AroundTimeout.

    Having said that, I hope that the use case you describe is only an example. As a matter of fact I find it bad practice to use an interceptor to change the behavior of the program as radically as you describe. I would find it very hard to reason about the functionality of such a program. Interceptors are good to implement cross-cutting concerns (e.g. transactions, logging, authorization) that do not mess with the core logic of the intercepted code.

    If I were to implement this use case I would:


    Since it proved to be useful, I am briefly re-iterating the main points of the JEE tutorial on interceptors:

    WARNING: the following is a brief summary, please follow the tutorials and documentation for concrete usage

    To define an interceptor, use one of the interceptor metadata annotations: javax.interceptor.AroundConstruct, javax.interceptor.AroundInvoke, javax.interceptor.AroundTimeout, javax.annotation.PostConstruct, javax.annotation.PreDestroy on a method defined on a component that the container will recognize. Could be the same component as the one containing the intercepted method or a different one.

    If the interceptor is in a different class, use javax.interceptor.Interceptors to activate it. This annotation can go to a specific method, or on an entire class, in which case all business methods are intercepted. Remember, interceptors do not take effect on non-business methods (private/protected ones are definitely NOT business and a common mistake). Interceptors do NOT take effect when business calling methods of this object, another very common mistake, beware!

    Alternatively you can define an interceptor binding annotation, e.g. (from the tutorial):

    @InterceptorBinding // this makes it an interceptor binding annotation
    @Target({TYPE, METHOD})
    @Retention(RUNTIME)
    @Inherited
    pubic @interface Logged { ... }
    

    Now annotating a class or method with @Logged will activate the interceptor defined as:

    @Logged
    @Interceptor
    public class LoggingInterceptor {
        @AroundInvoke
        public Object logInvocation(InvocationContext ctx) throws Exception {
            ...
        }
        ...
    }
    

    To actually activate the interceptor you have to use the deployment descriptor (depending on whether you are using it on EJB or CDI or both) or use the @Priority annotation.