eclipse-aspectj / aspectj

Other
303 stars 86 forks source link

No transactional EntityManager available after upgrading to version 1.9.22.1 #319

Closed smitzkus closed 2 weeks ago

smitzkus commented 3 weeks ago

I configured my maven build as follows to weave aspects of spring-aspects and spring-security-aspects:

<plugin>
        <groupId>dev.aspectj</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>${aspectj.maven.plugin.version}</version>
        <configuration>
          <proc>none</proc>
          <showWeaveInfo>true</showWeaveInfo>
          <Xlint>ignore</Xlint>
          <complianceLevel>17</complianceLevel>
          <encoding>UTF-8</encoding>
          <verbose>false</verbose>
          <outxml />
          <forceAjcCompile>true</forceAjcCompile>
          <parameters>true</parameters>
          <aspectLibraries>
            <aspectLibrary>
              <groupId>org.springframework</groupId>
              <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
            <aspectLibrary>
              <groupId>org.springframework.security</groupId>
              <artifactId>spring-security-aspects</artifactId>
            </aspectLibrary>
          </aspectLibraries>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>test-compile</goal>
            </goals>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
        </dependencies>
      </plugin>

The following code worked fine till version 1.9.19.:

    @PersistenceContext
    protected EntityManager entityManager;

    @Async(EXECUTOR)
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private Future<Boolean> process() {
        SharedSessionContractImplementor session = getSession();
        session.accessTransaction();
        ...
       return CompletableFuture.completedFuture(true);
    }

    protected SharedSessionContractImplementor getSession() {
        return entityManager.unwrap(SharedSessionContractImplementor.class);
    }

After upgrading to version 1.9.22.1 calling the method getSession causes the following exception:

java.lang.IllegalStateException: No transactional EntityManager available
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:291)
    at jdk.proxy3/jdk.proxy3.$Proxy180.unwrap(Unknown Source)

I use spring 6.1.14 and hibernate 6.6.1.Final. Could anyone help with this issue?

kriegaex commented 3 weeks ago

First, please note my message a while ago to the aspectj-users and aspectj-announce mailing lists regarding my situation as an AspectJ maintainer.

Having said that, please document your issue as well as possible, specifically by posting a link to a GitHub project containing a minimal reproducer. Then, as soon as someone else or I wants to dedicate time to the issue, we can dig right in without further ado. Thank you. 🙂

smitzkus commented 2 weeks ago

Hi Alexander, thank you very much for the quick reply! Unfortunately I couldn't reproduce the issue in isolation. I created the following spring-boot application including aspectj 1.9.22.1: https://github.com/smitzkus/spring-boot-aop-transactions

In class ProcessingService you can see the method 'process' annotated with @Transactional and @Async. Within this method i try to obtain the EntityManagerHolder via TransactionSynchronizationManager#getResource. In the demo everything is working fine (including calling entityManager#unwrap(SharedSessionContractImplementor.class which also uses TransactionSynchronizationManager#getResource).

In our production code calling TransactionSynchronizationManager#getResource returns null and calling entityManager#unwrap causes the IllegalStateException described above.

Meanwhile i was able to figure out, that only the AnnotationAsyncExecutionAspect was weaved in but not the AnnotationTransactionAspect (both from spring-aspect) as you can see in the stacktrace: 2024-11-08 14_49_36-2024-06 - TUG - kautionen-server_src_main_java_de_hausbank_kautionen_server_serv

Comparing the stacktrace with aspectj 1.9.19 shows the difference: 2024-11-08 15_35_20-2024-06 - TUG - kautionen-server_src_main_java_de_hausbank_kautionen_server_serv

Strangely, the aspect AnnotationTransactionAspect is listed in the weave-info output for the relevant class:

[INFO] Join point 'method-execution(java.util.concurrent.Future de.hausbank.kautionen.server.service.auswertung.AuswertungProcessingService.processDruckAuftragAuswertung(long))' in Type 'de.hausbank.kautionen.server.service.auswertung.AuswertungProcessingService' (AuswertungProcessingService.java:124) advised by around advice from 'org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect' (spring-aspects-6.1.14.jar!AbstractAsyncExecutionAspect.class:65(from AbstractAsyncExecutionAspect.aj))
[INFO] Join point 'method-execution(java.util.concurrent.Future de.hausbank.kautionen.server.service.auswertung.AuswertungProcessingService.processDruckAuftragAuswertung(long))' in Type 'de.hausbank.kautionen.server.service.auswertung.AuswertungProcessingService' (AuswertungProcessingService.java:124) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (spring-aspects-6.1.14.jar!AbstractTransactionAspect.class:67(from AbstractTransactionAspect.aj))

My hope is that you could give me a hint based on the changes between version 1.9.19 and 1.9.20, when the problem first appeared. Or are there other ways of debugging to find out why the AnnotationTransactionAspect gets not weaved in?

I really would appreciate any help!

kriegaex commented 2 weeks ago

Unfortunately I couldn't reproduce the issue in isolation. I created the following spring-boot application including aspectj 1.9.22.1: https://github.com/smitzkus/spring-boot-aop-transactions

Hm, a sample project which does not reproduce the problem is not super helpful. Your problem seems to be quite specific to Spring aspects. I am not a Spring user, but using @Transactional and @Async on the same method seems to be discouraged, see e.g.:

It looks as if Spring aspects have an undefined execution order by default. Maybe, slightly refactoring your code according to the descriptions there will solve the problem. I recommend trying that. Another workaround would be to add an aspect with declare precedence (native syntax) or @DeclarePrecedence in it, specifying aspect precedence explicitly, and compiling it together with the rest of your code using AspectJ Maven. But that is only plan B, I would try the simple approach described on the first two linked pages.

My hope is that you could give me a hint based on the changes between version 1.9.19 and 1.9.20, when the problem first appeared.

I am not sure it is an AspectJ problem. Did you actually check the class files to verify that one of the the aspects has not been woven? The stack trace does not prove anything in this case. It just shows a runtime call stack.

Did you verify that it is actually 1.9.20 in which the change of behaviour happens? You wrote that you use 1.9.22.1. Anyway, you can see the list of changes for each release linked off of the corresponding release notes, e.g. https://github.com/eclipse-aspectj/aspectj/blob/master/docs/release/README-1.9.20.adoc.

I cannot be more specific in helping you, if I have no reproducer.

smitzkus commented 2 weeks ago

Thank you so much for your help, using "declare precedence" solved my problem. I don't know why but the order of the aspects 'AnnotationAsyncExecutionAspect' and 'AnnotationTransactionAspect' changed after upgrading from version 1.9.19 to 1.9.20 or higher. I have to make sure that aspect AnnotationAsyncExecutionAspect is called before AnnotationTransactionAspect. Otherwise no transaction is bound to the executor which then causes the "No transactional EntityManager available" exception.

I updated my demo using @DeclarePrecedence with the wrong order. Now the exception is reproducible.

kriegaex commented 2 weeks ago

I am happy it works for you now.