Search code examples
javarestjax-rsjersey-2.0

Jersey: Detect when Controller Class is Created


I've implemented a JAX-RS server application using Jersey 2.24.

I use the Guice-HK2 bridge so that the controller classes (those annotated with @Path) are injected with dependencies from Guice, not Jersey/HK2.

However, HK2 still creates instances of the @Path annotated classes itself.

Is there a way I can plug into Jersey/HK2 so that I'm notified when a @Path annotated class is created? Like some sort of lifecycle listener? Every time a @Path annotated class is created by Jersey/HK2 I want to do some registering/logging of that class.

If Guice were doing the actual creation of the @Path annotated class I think I could do it using a generic Provider but that's not available in this case, since Jersey/HK2 is creating the actual instance.

Thank you!!


Solution

  • I think the least intrusive way would be to just use AOP. HK2 offers AOP. What you can do is create a ConstructorInterceptor. Something like

    public class LoggingConstructorInterceptor implements ConstructorInterceptor {
    
        private static final Logger LOG
                = Logger.getLogger(LoggingConstructorInterceptor.class.getName());
    
        @Override
        public Object construct(ConstructorInvocation invocation) throws Throwable {
            Constructor ctor = invocation.getConstructor();
            LOG.log(Level.INFO, "Creating: {0}", ctor.getDeclaringClass().getName());
    
            // returned instance from constructor invocation.
            Object instance = invocation.proceed();
            LOG.log(Level.INFO, "Created Instance: {0}", instance.toString());
    
            return instance;
        }
    }
    

    Then create a InterceptorService to only use the interceptor for classes annotated with @Path

    public class PathInterceptionService implements InterceptionService {
    
        private static final ConstructorInterceptor CTOR_INTERCEPTOR
                = new LoggingConstructorInterceptor();
        private final static List<ConstructorInterceptor> CTOR_LIST
                = Collections.singletonList(CTOR_INTERCEPTOR);
    
        @Override
        public Filter getDescriptorFilter() {
            return BuilderHelper.allFilter();
        }
    
        @Override
        public List<MethodInterceptor> getMethodInterceptors(Method method) {
            return null;
        }
    
        @Override
        public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> ctor) {
            if (ctor.getDeclaringClass().isAnnotationPresent(Path.class)) {
                return CTOR_LIST;
            }
            return null;
        }
    }
    

    Then just register the InterceptionService and ConstructorInterceptor with the DI system

    new ResourceConfig()
            .register(new AbstractBinder(){
                @Override
                public void configure() {
                    bind(PathInterceptionService.class)
                            .to(InterceptionService.class)
                            .in(Singleton.class);
                        bind(LoggingConstructorInterceptor.class)
                            .to(ConstructorInterceptor.class)
                            .in(Singleton.class);
                }
            });
    

    See complete example in this Gist

    See Also: