Kevin Krumwiede
Kevin Krumwiede

Reputation: 10288

Crashes related to GraphRepository#findAll() when using AspectJ

This line in TopLevelTransaction (neo4j-kernel-2.1.2) throws a NullPointerException every time I call next() on an iterator obtained via GraphRepository#findAll():

protected void markAsRollbackOnly()
{
    try
    {
        transactionManager.getTransaction().setRollbackOnly(); // NPE here
    }
    catch ( Exception e )
    {
        throw new TransactionFailureException(
            "Failed to mark transaction as rollback only.", e );
    }
}

I found some threads about similar crashes with slightly different stack traces. The accepted solution on this question is to use "proxy" transaction management, but that seems like a band-aid solution. This question also mentions "proxy" transaction management and suggests that there might be something wrong with the @Transactional annotation when using AspectJ.

Is this legitimately a bug, or have I just set up my project incorrectly? My code is essentially the same as in my standalone hello world, with a slightly more complex main class:

@Component
public class Test2 {
    @Autowired
    FooRepository repo;

    public static void main(String[] args) {
        AbstractApplicationContext context = new AnnotationConfigApplicationContext("test2");
        Test2 test2 = context.getBean(Test2.class);
        test2.doStuff();
    }

    public void doStuff() {
        createFoo();
        printFoos();
    }

    @Transactional
    public Foo createFoo() {
        Foo foo = new Foo();
        foo.setName("Derp" + System.currentTimeMillis());
        repo.save(foo);
        System.out.println("saved " + foo.toString());
        return foo;
    }

    @Transactional
    public void printFoos() {
        Iterable<Foo> foos = repo.findAll();
        System.out.println("findAll() returned instance of " + foos.getClass().getName());
        Iterator<Foo> iter = foos.iterator();
        System.out.println("iterator is instance of " + iter.getClass().getName());
        if(iter.hasNext()) {
            iter.next(); // CRASHES HERE
        }
    }
}

I can post my POM if needed.

Upvotes: 1

Views: 208

Answers (1)

Kevin Krumwiede
Kevin Krumwiede

Reputation: 10288

I didn't find a bug. Two or three things are required to make this work, depending on whether you want to use proxy or AspectJ transaction management.

First, transaction management must be enabled. Since I'm using annotation-based configuration, I did this by annotating my @Configuration class with @EnableTransactionManagement. Contrary to the docs, the default mode now seems to be AdviceMode.ASPECTJ, not AdviceMode.PROXY.

Next, you need to ensure that the Iterator is used within a transaction. In my example, if I use AdviceMode.PROXY the entire bean containing the @Autowired repository has to be annotated @Transactional. If I use AdviceMode.ASPECTJ I can annotate just the method. This is because the call to the method using the iterator is a self-call from within the bean, and proxy transaction management cannot intercept and manage internal calls.

Finally, if you're using AdviceMode.ASPECTJ you must set up weaving as discussed here.

Upvotes: 2

Related Questions