guiceinterceptorguice-persist

How to debug Guice class/method interceptors?


I'm trying to use guice-persist and I can't get it to work in a simple test case. This a generic request handler which is getting called from a dispatcher:

public abstract class TransactionAwareRequestHandler<T> extends SyncRequestHandler<T> {

    @Transactional // Guice should make sure I get a transaction here
    public final Object handleRequest(T request) {
        return handleRequestWithTx(request);
    }

    public abstract Object handleRequestWithTx(T request);
}

My test handler is:

public static class TestHandler extends TransactionAwareRequestHandler {
    @Override
    public Object handleRequestWithTx(Object request) {
        SessionFactory sessionFactory = injector.getProvider(SessionFactory.class).get();
        assertTrue(sessionFactory.getCurrentSession().getTransaction().isActive()); // <<<--- Fails here!
        return null;
    }
}

Guice setup:

    injector = Guice.createInjector(
        new HibernatePersistModule()
    );

This will add a bind interceptor for all classes and methods annotated with @Transactional

Test case:

@Test
public void shouldRunInTransaction() throws RequestHandlerException {
    TestHandler handler = injector.getInstance(TestHandler.class);
    
    // I'm not getting a proxy here! Why???
    assertTrue("Expected proxy: " + handler.getClass(),
            Proxy.isProxyClass(handler.getClass()));
    
    handler.handleRequest(null);
}

Questions:

Why does this happen?

How can I debug this to find out why it's happening? Is there some way to add logging to the class/method matcher?


Solution

  • There are several problems here:

    1. Java can't create proxies of final classes or methods. Guice should give an error or warning in this case but, for some reason, doesn't.
    2. The annotation isn't on the class but a superclass. The standard method matcher just takes methods of the class into account. If you want a different behavior, you need this matcher: https://stackoverflow.com/a/61674578/34088

    To debug what Guice is doing, wrap the matcher in one which does logging:

    import java.util.Objects;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.google.inject.matcher.AbstractMatcher;
    import com.google.inject.matcher.Matcher;
    
    public class MatcherLogger<T> extends AbstractMatcher<T> {
    
        private final Matcher<T> delegate;
        private final Logger log;
        
        public MatcherLogger(Matcher<T> delegate) {
            this(delegate, LoggerFactory.getLogger(MatcherLogger.class));
        }
    
        public MatcherLogger(Matcher<T> delegate, Logger log) {
            this.delegate = Objects.requireNonNull(delegate);
            this.log = Objects.requireNonNull(log);
        }
        
        @Override
        public boolean matches(T t) {
            boolean result = delegate.matches(t);
            log.debug("matches {} for {}", result, t);
            return result;
        }
    }