0

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?

Aaron Digulla
  • 297,790
  • 101
  • 558
  • 777
  • I think this happens because Guice doesn't check inherited methods at all. When I put my own method interceptor there, I can see that Guice doesn't try to match the interceptor against `TransactionAwareRequestHandler.handleRequest`. Only `TestHandler.handleRequestWithTx` shows up in the debug output. Why is that? – Aaron Digulla Aug 24 '20 at 11:57
  • 1
    You can't apply @Annotations to final classes or methods (nor private, although that's not an issue here). – kendavidson Aug 24 '20 at 19:07
  • Hmm... good point. I'd just expect Guice to give me an error or warning. – Aaron Digulla Aug 26 '20 at 11:58

1 Answers1

1

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;
    }
}
Aaron Digulla
  • 297,790
  • 101
  • 558
  • 777