From 9430b24eaf6cd8c350b760748151995c98b34af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Thu, 22 Feb 2024 16:10:32 +0100 Subject: [PATCH] Adapt Hibernate native support for HHH-17643 This commit adapts Hibernate native support to handle the changes performed as part of HHH-17643 which impacts Hibernate versions 6.4.3+ and 6.2.21+. It ignores the BytecodeProvider services loaded by the service loader feature in order to default to the "no-op" provider with native, and makes the substitutions more lenient when a substituted field or method does not exist. gh-32314 is expected to remove the need for such substitutions which are not great for maintainability by design. Closes gh-32311 --- .../jpa/vendor/SubstituteOnlyIfPresent.java | 39 ------------------- .../jpa/vendor/Target_BytecodeProvider.java | 19 ++++++++- .../Target_BytecodeProviderInitiator.java | 23 ++++++++++- .../spring-orm/native-image.properties | 1 + 4 files changed, 40 insertions(+), 42 deletions(-) delete mode 100644 spring-orm/src/main/java/org/springframework/orm/jpa/vendor/SubstituteOnlyIfPresent.java create mode 100644 spring-orm/src/main/resources/META-INF/native-image/org.springframework/spring-orm/native-image.properties diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/SubstituteOnlyIfPresent.java b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/SubstituteOnlyIfPresent.java deleted file mode 100644 index 57473cc3f2c..00000000000 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/SubstituteOnlyIfPresent.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.orm.jpa.vendor; - -import java.util.function.Predicate; - -/** - * Predicate intended to enable the related GraalVM substitution only when the class is present on the classpath. - * - * @author Sebastien Deleuze - * @since 6.1 - */ -class SubstituteOnlyIfPresent implements Predicate { - - @Override - public boolean test(String type) { - try { - Class.forName(type, false, getClass().getClassLoader()); - return true; - } - catch (ClassNotFoundException | NoClassDefFoundError ex) { - return false; - } - } -} diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProvider.java b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProvider.java index 50c90551b4a..df7aac17cf0 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProvider.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProvider.java @@ -17,6 +17,7 @@ package org.springframework.orm.jpa.vendor; import java.util.Map; +import java.util.function.Predicate; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -31,11 +32,27 @@ import org.hibernate.property.access.spi.PropertyAccess; * @since 6.1 * @see HHH-17568 */ -@TargetClass(className = "org.hibernate.bytecode.internal.none.BytecodeProviderImpl", onlyWith = SubstituteOnlyIfPresent.class) +@TargetClass(className = "org.hibernate.bytecode.internal.none.BytecodeProviderImpl", onlyWith = Target_BytecodeProvider.SubstituteOnlyIfPresent.class) final class Target_BytecodeProvider { @Substitute public ReflectionOptimizer getReflectionOptimizer(Class clazz, Map propertyAccessMap) { return null; } + + static class SubstituteOnlyIfPresent implements Predicate { + + @Override + public boolean test(String type) { + try { + Class clazz = Class.forName(type, false, getClass().getClassLoader()); + clazz.getDeclaredMethod("getReflectionOptimizer", Class.class, Map.class); + return true; + } + catch (ClassNotFoundException | NoClassDefFoundError | NoSuchMethodException ex) { + return false; + } + } + } + } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProviderInitiator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProviderInitiator.java index c2c767afc99..64ca048af44 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProviderInitiator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/Target_BytecodeProviderInitiator.java @@ -16,6 +16,8 @@ package org.springframework.orm.jpa.vendor; +import java.util.function.Predicate; + import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; @@ -27,12 +29,11 @@ import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; /** * Hibernate substitution designed to prevent ByteBuddy reachability on native, and to enforce the * usage of {@code org.hibernate.bytecode.internal.none.BytecodeProviderImpl} with Hibernate 6.3+. - * TODO Collaborate with Hibernate team on a substitution-less alternative that does not require a custom list of StandardServiceInitiator * * @author Sebastien Deleuze * @since 6.1 */ -@TargetClass(className = "org.hibernate.bytecode.internal.BytecodeProviderInitiator", onlyWith = SubstituteOnlyIfPresent.class) +@TargetClass(className = "org.hibernate.bytecode.internal.BytecodeProviderInitiator", onlyWith = Target_BytecodeProviderInitiator.SubstituteOnlyIfPresent.class) final class Target_BytecodeProviderInitiator { @Alias @@ -46,4 +47,22 @@ final class Target_BytecodeProviderInitiator { public static BytecodeProvider buildBytecodeProvider(String providerName) { return new org.hibernate.bytecode.internal.none.BytecodeProviderImpl(); } + + static class SubstituteOnlyIfPresent implements Predicate { + + @Override + public boolean test(String type) { + try { + Class clazz = Class.forName(type, false, getClass().getClassLoader()); + clazz.getDeclaredMethod("buildBytecodeProvider", String.class); + clazz.getField("BYTECODE_PROVIDER_NAME_NONE"); + clazz.getField("BYTECODE_PROVIDER_NAME_DEFAULT"); + return true; + } + catch (ClassNotFoundException | NoClassDefFoundError | NoSuchMethodException | NoSuchFieldException ex) { + return false; + } + } + } + } diff --git a/spring-orm/src/main/resources/META-INF/native-image/org.springframework/spring-orm/native-image.properties b/spring-orm/src/main/resources/META-INF/native-image/org.springframework/spring-orm/native-image.properties new file mode 100644 index 00000000000..ae2375f2645 --- /dev/null +++ b/spring-orm/src/main/resources/META-INF/native-image/org.springframework/spring-orm/native-image.properties @@ -0,0 +1 @@ +Args = -H:ServiceLoaderFeatureExcludeServices=org.hibernate.bytecode.spi.BytecodeProvider