quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.75k stars 2.67k forks source link

Hibernate Proxy classes generated with incorrect java release version #13871

Closed turing85 closed 3 years ago

turing85 commented 3 years ago

Describe the bug We can set the java release version to be used through maven property maven.compiler.release (e.g. <maven.compiler.release>11</maven.compiler.release>)

When the project is then compiled with a javac version >= 11 and run with java 11, an java.lang.UnsupportedClassVersionError is thrown on application startup.

Expected behavior All sources should be compiled to target the specified java release, and the application should start up.

Actual behavior On application startup, the following exception is thrown:

Exception in thread "main" java.lang.ExceptionInInitializerError
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source)
    at java.base/java.lang.Class.newInstance(Unknown Source)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:61)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:38)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:104)
    at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
Caused by: java.lang.RuntimeException: Failed to start quarkus
    at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:249)
    ... 9 more
Caused by: java.lang.UnsupportedClassVersionError: de/turing85/HelloEntity$HibernateProxy$0mzonqkm has been compiled by a more recent version of the Java Runtime (class file version 58.0), this version of the Java Runtime only recognizes class file versions up to 55.0
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
    at java.base/java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(Unknown Source)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Unknown Source)
    at io.quarkus.hibernate.orm.runtime.proxies.ProxyDefinitions.generateProxyClass(ProxyDefinitions.java:127)
    at io.quarkus.hibernate.orm.runtime.proxies.ProxyDefinitions.createFromMetadata(ProxyDefinitions.java:61)
    at io.quarkus.hibernate.orm.runtime.boot.FastBootMetadataBuilder.build(FastBootMetadataBuilder.java:318)
    at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.createMetadata(PersistenceUnitsHolder.java:101)
    at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.constructMetadataAdvance(PersistenceUnitsHolder.java:73)
    at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.initializeJpa(PersistenceUnitsHolder.java:40)
    at io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder$1.created(HibernateOrmRecorder.java:58)
    at io.quarkus.arc.runtime.ArcRecorder.initBeanContainer(ArcRecorder.java:53)
    at io.quarkus.deployment.steps.ArcProcessor$generateResources-1025303321.deploy_0(ArcProcessor$generateResources-1025303321.zig:127)
    at io.quarkus.deployment.steps.ArcProcessor$generateResources-1025303321.deploy(ArcProcessor$generateResources-1025303321.zig:40)
    at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:207)
    ... 9 more

To Reproduce

Environment (please complete the following information):

Additional Information: The problem starts occurring if and only if quarkus.datasource.db-kind is set.

ghost commented 3 years ago

/cc @Sanne, @gsmet

stuartwdouglas commented 3 years ago

@Sanne looks like hibernate generates proxies with the version of the VM that runs augment, with no way to change it (it would need to be an additional ctor param to BytecodeProviderImpl which would need to pass it into ByteBuddyState).

Sanne commented 3 years ago

thanks for the analysis @stuartwdouglas and @turing85 !

But general question first. Should we really support the case of people running an older JDK at runtime than the one used during our augmentation phase?

We can certainly improve things, but I wonder what kind of rabbit hole this brings us into. Perhaps a global upfront check at framework level would be a better direction?

turing85 commented 3 years ago

@Sanne scenario, in which I ran into the problem:

I certainly agree that, when built through a pipeline, the java-version building the artifact should match the java-version running the artifact. But for local development, it should be possible to use a more recent java-release. Furthermore, we have a dedicated compiler-flag (--release) to fixate the java-version we target, so why not use it?

Sanne commented 3 years ago

@turing85 yes I agree, all good points and we can certainly improve things in the Hibernate extensions.

I'm just concerned that this won't be the last of such issues even if we solve it - we might need a more comprehensive approach.

@stuartwdouglas what about MR jars (for example?): will we need to ensure the classloaders load the right class for the target deployment version (which is apparently not the same thing as the version used at augmentation)

stuartwdouglas commented 3 years ago

@Sanne while there are issues around this, IMHO I think this one is easy to fix. As I understand it the proxies in question won't be using any class file features introduced after JDK8, so it is easiest just to hard code the generated class format to JDK8.

We have seen other similar errors, and we recommend that you build and deploy with the same JDK, but as much as possible we have been fixing these on a 'best effort' basis when they crop up.

Sanne commented 3 years ago

pushed this to Hibernate ORM:

Will be included in upcoming 5.4.27.Final release - then we need to follow up with some Quarkus patch which sets the expected build version.