Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CGLIB runtime enhancement not supported on native image #30939

Closed
danthe1st opened this issue Jul 24, 2023 · 13 comments
Closed

CGLIB runtime enhancement not supported on native image #30939

danthe1st opened this issue Jul 24, 2023 · 13 comments
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: duplicate A duplicate of another issue theme: aot An issue related to Ahead-of-time processing type: regression A bug that is also a regression

Comments

@danthe1st
Copy link

danthe1st commented Jul 24, 2023

Affects: Spring 6.0.11 / Spring Boot 3.1.2


For reproducing the issue, I highly recommend starting with the attached reproducer linked below.

Assume an application with the following components:

  • Spring Boot 3.1.2
  • Spring JPA
  • Spring Security
  • Spring Web
  • Spring test
  • Spring Security test
  • H2 as a database (for tests, I don't think the DB affects this issue)

Let this application have a controller with an endpoint accepting a MultipartFile RequestParam that calls an @Transactional method.
Add an entity with a String field (@Id) and a CrudRepository with a getBy<The string field>.
Add an ApplicationListener<ApplicationReadyEvent> which calls the above method.
Add a @SpringBootTest with two @Test methods, one of them annotated with @WithMockUser. These test methods can be empty.

Run the tests using native-image with mvn -PnativeTest test. An exception like the following should occur:

java.lang.IllegalStateException: Failed to load ApplicationContext for [AotMergedContextConfiguration@5207f8d0 testClass = io.github.danthe1st.spring_test.SpringTestApplicationTests, contextInitializerClass = io.github.danthe1st.spring_test.SpringTestApplicationTests__TestContext001_ApplicationContextInitializer, original = [WebMergedContextConfiguration@6b119ba0 testClass = io.github.danthe1st.spring_test.SpringTestApplicationTests, locations = [], classes = [io.github.danthe1st.spring_test.SpringTestApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@64e953ce, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@29866a5b, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@329bad59, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@24c2dbca, org.springframework.boot.test.context.SpringBootTestAnnotation@6c69f2e0], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]]
       org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:143)
       org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:127)
       org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191)
       org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130)
       org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:241)
       [...]
     Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someController': Unexpected AOP exception
       org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:605)
       org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
       org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
       org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
       org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
       [...]
     Caused by: org.springframework.aop.framework.AopConfigException: Unexpected AOP exception
       org.springframework.aop.framework.CglibAopProxy.buildProxy(CglibAopProxy.java:228)
       org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:155)
       org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
       org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.buildProxy(AbstractAutoProxyCreator.java:517)
       org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:464)
       [...]
     Caused by: java.lang.UnsupportedOperationException: CGLIB runtime enhancement not supported on native image. Make sure to include a pre-generated class on the classpath instead: io.github.danthe1st.spring_test.SomeController$$SpringCGLIB$$1
       org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)
       org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:575)
       org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107)
       org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52)
       java.base@17.0.5/java.util.concurrent.FutureTask.run(FutureTask.java:264)
       [...]

Reproducer: https://github.com/danthe1st/spring-cglib-repro/actions/runs/5648996009
Build log with error: https://github.com/danthe1st/spring-cglib-repro/actions/runs/5648996009/job/15302392597

This issue is similar to #30937 but the error is different so I decided to report both issues.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jul 24, 2023
@snicoll
Copy link
Member

snicoll commented Jul 25, 2023

same as #30937 (comment)

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Jul 25, 2023
@danthe1st
Copy link
Author

When trying to reproduce the issue, it only happened with @Transactional (without it, I got to the other issue).
MultipartFile is actually not required (I thought it was required because removing it caused the issue to disappear at some point in the process of creating the reproducer).
I also added the enhancement plugin to the reproducer (this doesn't change anything and I have previously removed it so I can reduce the reproducer as much as possible).
I assume that @Transactional causes the proxy class to be required/generated (at runtime).

The reproducer is now updated.
The other issue is a bit different, I'll do similar steps there as well.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jul 25, 2023
@snicoll
Copy link
Member

snicoll commented Jul 25, 2023

Thanks. The runtime expects io.github.danthe1st.spring_test.SomeController$$SpringCGLIB$$1 but only io.github.danthe1st.spring_test.SomeController$$SpringCGLIB$$0 has been generated. It looks like a second proxy is required that isn't discovered by AOT.

@jhoeller any idea how we could debug this?

@danthe1st
Copy link
Author

Output of the mvn test -PnativeTest command (for the future in case the GitHub Actions log expired):
build.log

@sbrannen sbrannen added in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing labels Aug 25, 2023
@danthe1st
Copy link
Author

Is there any chance in getting this finding out how this can be debugged/finding out why the second proxy would be necessary/why it isn't generated?
To me, this seems like an issue that would be applicable in many cases.

@sbrannen
Copy link
Member

@danthe1st, what happens if you remove hibernate-enhance-maven-plugin from pom.xml?

@danthe1st
Copy link
Author

Originally, I didn't have the hibernate enhancer plugin in my reproducer and I just added it after #30939 (comment) but I will update it.

@danthe1st
Copy link
Author

I have removed the plugin in a branch named cglib-no-hibernate-enhancer in my reproducer. You can find the build log here.
As you can see, the error still occurs.

@sbrannen

@snicoll
Copy link
Member

snicoll commented Sep 17, 2023

@danthe1st to answer your question, we'll have to debug it to understand why the second proxy was not generated at build-time. It's high on my list and I hope to get to that next week.

@snicoll snicoll self-assigned this Sep 17, 2023
@sbrannen
Copy link
Member

we'll have to debug it to understand why the second proxy was not generated at build-time. It's high on my list and I hope to get to that next week.

I believe this may actually be a duplicate of #31238 (see #31238 (comment)).

In any case, I think the underlying problem is that a new proxy class is generated for (what should be) the same key, which might imply that there is something wrong with the key we generate.

@danthe1st
Copy link
Author

I want to note that in contrast to #31238, this issue only seems to occur with Spring Boot 3.1.x and not with 3.0.x (if I remember correctly)

@sbrannen
Copy link
Member

I have confirmed that the local fix I have in place for #31238 allows the native image tests for the example project for this issue to pass using Spring Boot 3.1.2 and Spring Framework 6.0.13-SNAPSHOT.

In light of that, I am closing this issue as a:

@sbrannen sbrannen closed this as not planned Won't fix, can't repro, duplicate, stale Sep 19, 2023
@sbrannen sbrannen added status: duplicate A duplicate of another issue type: regression A bug that is also a regression and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Sep 19, 2023
@sbrannen
Copy link
Member

It turns out this issue was not only a duplicate of #31238 but also a:

Although I knew part of this issue was resolved in 6.0.13-SNAPSHOT, I wasn't sure what had actually caused the second attempt to create the @Transactional proxy class; whereas, in #31238 it was clearly due to the use of @DirtiesContext.

So I decided to debug the sample application for this issue further, and in doing so I noticed that #31050 was causing the first attempt to load the test's ApplicationContext to fail, and that's why the second @Test method resulted in a 2nd attempt to load the ApplicationContext, thereby displaying the same behavior as in #31238.

With that, the mystery is solved!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: duplicate A duplicate of another issue theme: aot An issue related to Ahead-of-time processing type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

4 participants