From 2b6291250f9b57bc3ce5f8e9407d234a6c89cd61 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 17 Feb 2023 15:20:45 -0800 Subject: [PATCH] Replace more uses of `ClassLoadingStrategy.Default.INJECTION` (#23210) * Replace more uses of `ClassLoadingStrategy.Default.INJECTION` in preparation for Java 17+. Follow-up to: https://github.com/apache/beam/commit/98f1f75459ee300baa5574042149a65063239705. * Move helper method to a shared ByteBuddyUtils class * spotlessApply * Apply suggestions from code review Co-authored-by: Lukasz Cwik * Fix nullness issues, add an overload that takes a custom classloader * spotless apply * Fix imports * Update sdks/java/core/src/main/java/org/apache/beam/sdk/util/ByteBuddyUtils.java Co-authored-by: Lukasz Cwik * Use ISE instead of LinkageError * Add a missing import * s/getClassLoader/findClassLoader * fix Checker Framework error --------- Co-authored-by: Lukasz Cwik --- .../beam/sdk/coders/RowCoderGenerator.java | 7 ++- .../sdk/schemas/utils/AvroByteBuddyUtils.java | 5 +- .../sdk/schemas/utils/ByteBuddyUtils.java | 4 +- .../beam/sdk/schemas/utils/JavaBeanUtils.java | 11 ++-- .../beam/sdk/schemas/utils/POJOUtils.java | 10 ++-- .../schemas/utils/SelectByteBuddyHelpers.java | 7 ++- .../ByteBuddyOnTimerInvokerFactory.java | 10 +--- .../apache/beam/sdk/util/ByteBuddyUtils.java | 57 +++++++++++++++++++ 8 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 sdks/java/core/src/main/java/org/apache/beam/sdk/util/ByteBuddyUtils.java diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/coders/RowCoderGenerator.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/coders/RowCoderGenerator.java index 5ce7358d8824e..5a37a22e22dcd 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/coders/RowCoderGenerator.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/coders/RowCoderGenerator.java @@ -17,6 +17,7 @@ */ package org.apache.beam.sdk.coders; +import static org.apache.beam.sdk.util.ByteBuddyUtils.getClassLoadingStrategy; import static org.apache.beam.vendor.guava.v26_0_jre.com.google.common.base.Preconditions.checkState; import java.io.IOException; @@ -36,7 +37,6 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription.ForLoadedType; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.scaffold.InstrumentedType; import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.Implementation; @@ -55,6 +55,7 @@ import org.apache.beam.sdk.schemas.Schema.Field; import org.apache.beam.sdk.schemas.Schema.FieldType; import org.apache.beam.sdk.schemas.SchemaCoder; +import org.apache.beam.sdk.util.common.ReflectHelpers; import org.apache.beam.sdk.values.Row; import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.base.Preconditions; import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.Maps; @@ -172,7 +173,9 @@ public static Coder generate(Schema schema) { rowCoder = builder .make() - .load(Coder.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .load( + ReflectHelpers.findClassLoader(schema.getClass().getClassLoader()), + getClassLoadingStrategy(schema.getClass())) .getLoaded() .getDeclaredConstructor(Coder[].class, int[].class) .newInstance((Object) componentCoders, (Object) encodingPosToRowIndex); diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java index eb01b83c94fc1..4c9055ba245c0 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java @@ -17,6 +17,8 @@ */ package org.apache.beam.sdk.schemas.utils; +import static org.apache.beam.sdk.util.ByteBuddyUtils.getClassLoadingStrategy; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; @@ -25,7 +27,6 @@ import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.type.TypeDescription.ForLoadedType; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.implementation.MethodCall; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.implementation.bytecode.assign.TypeCasting; @@ -99,7 +100,7 @@ private static SchemaUserTypeCreator createCreator(Class clazz, Schema sc .make() .load( ReflectHelpers.findClassLoader(clazz.getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(clazz)) .getLoaded() .getDeclaredConstructor() .newInstance(); diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java index bb0003c4d4dc0..65eaffb9318f7 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java @@ -17,6 +17,7 @@ */ package org.apache.beam.sdk.schemas.utils; +import static org.apache.beam.sdk.util.ByteBuddyUtils.getClassLoadingStrategy; import static org.apache.beam.vendor.guava.v26_0_jre.com.google.common.base.Preconditions.checkNotNull; import java.lang.reflect.Constructor; @@ -43,7 +44,6 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription.ForLoadedType; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.scaffold.InstrumentedType; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.Implementation.Context; @@ -459,7 +459,7 @@ public InstrumentedType prepare(InstrumentedType instrumentedType) { .make() .load( ReflectHelpers.findClassLoader(((Class) fromType).getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy((Class) fromType)) .getLoaded(); } diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java index ec0dba2c9421c..49d30f0e34549 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java @@ -17,6 +17,8 @@ */ package org.apache.beam.sdk.schemas.utils; +import static org.apache.beam.sdk.util.ByteBuddyUtils.getClassLoadingStrategy; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -28,7 +30,6 @@ import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.method.MethodDescription.ForLoadedMethod; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.scaffold.InstrumentedType; import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.Implementation; @@ -162,7 +163,7 @@ public static FieldValueGetter createGetter( .load( ReflectHelpers.findClassLoader( typeInformation.getMethod().getDeclaringClass().getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(typeInformation.getMethod().getDeclaringClass())) .getLoaded() .getDeclaredConstructor() .newInstance(); @@ -226,7 +227,7 @@ public static FieldValueSetter createSetter( .load( ReflectHelpers.findClassLoader( typeInformation.getMethod().getDeclaringClass().getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(typeInformation.getMethod().getDeclaringClass())) .getLoaded() .getDeclaredConstructor() .newInstance(); @@ -290,7 +291,7 @@ public static SchemaUserTypeCreator createConstructorCreator( .make() .load( ReflectHelpers.findClassLoader(clazz.getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(clazz)) .getLoaded() .getDeclaredConstructor() .newInstance(); @@ -338,7 +339,7 @@ public static SchemaUserTypeCreator createStaticCreator( .make() .load( ReflectHelpers.findClassLoader(clazz.getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(clazz)) .getLoaded() .getDeclaredConstructor() .newInstance(); diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java index 46970c6bc4f6f..47e2b4eec5f19 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java @@ -17,6 +17,8 @@ */ package org.apache.beam.sdk.schemas.utils; +import static org.apache.beam.sdk.util.ByteBuddyUtils.getClassLoadingStrategy; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -156,7 +158,7 @@ private static SchemaUserTypeCreator createSetFieldCreator( .make() .load( ReflectHelpers.findClassLoader(clazz.getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(clazz)) .getLoaded() .getDeclaredConstructor() .newInstance(); @@ -208,7 +210,7 @@ public static SchemaUserTypeCreator createConstructorCreator( .make() .load( ReflectHelpers.findClassLoader(clazz.getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(clazz)) .getLoaded() .getDeclaredConstructor() .newInstance(); @@ -299,7 +301,7 @@ public static SchemaUserTypeCreator createStaticCreator( .make() .load( ReflectHelpers.findClassLoader(field.getDeclaringClass().getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(field.getDeclaringClass())) .getLoaded() .getDeclaredConstructor() .newInstance(); @@ -379,7 +381,7 @@ private static FieldValueSetter createSetter( .make() .load( ReflectHelpers.findClassLoader(field.getDeclaringClass().getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + getClassLoadingStrategy(field.getDeclaringClass())) .getLoaded() .getDeclaredConstructor() .newInstance(); diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/SelectByteBuddyHelpers.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/SelectByteBuddyHelpers.java index fd3b3735ee5ce..ec3db9b3cce4a 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/SelectByteBuddyHelpers.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/SelectByteBuddyHelpers.java @@ -17,6 +17,7 @@ */ package org.apache.beam.sdk.schemas.utils; +import static org.apache.beam.sdk.util.ByteBuddyUtils.getClassLoadingStrategy; import static org.apache.beam.vendor.guava.v26_0_jre.com.google.common.base.Preconditions.checkNotNull; import com.google.auto.value.AutoValue; @@ -36,7 +37,6 @@ import net.bytebuddy.description.type.TypeDescription.ForLoadedType; import net.bytebuddy.description.type.TypeDescription.Generic; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.scaffold.InstrumentedType; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.Implementation.Context; @@ -66,6 +66,7 @@ import org.apache.beam.sdk.schemas.Schema.FieldType; import org.apache.beam.sdk.schemas.utils.ByteBuddyUtils.IfNullElse; import org.apache.beam.sdk.schemas.utils.ByteBuddyUtils.ShortCircuitReturnNull; +import org.apache.beam.sdk.util.common.ReflectHelpers; import org.apache.beam.sdk.values.Row; import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.base.Preconditions; import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.Lists; @@ -177,7 +178,9 @@ static RowSelector createRowSelector(SchemaAndDescriptor schemaAndDescriptor) { return builder .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES)) .make() - .load(Row.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .load( + ReflectHelpers.findClassLoader(schemaAndDescriptor.getClass().getClassLoader()), + getClassLoadingStrategy(schemaAndDescriptor.getClass())) .getLoaded() .getDeclaredConstructor(Schema.class) .newInstance(outputSchema); diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyOnTimerInvokerFactory.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyOnTimerInvokerFactory.java index 49affe696e463..09e6212fa661f 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyOnTimerInvokerFactory.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyOnTimerInvokerFactory.java @@ -17,6 +17,7 @@ */ package org.apache.beam.sdk.transforms.reflect; +import static org.apache.beam.sdk.util.ByteBuddyUtils.getClassLoadingStrategy; import static org.apache.beam.sdk.util.common.ReflectHelpers.findClassLoader; import java.lang.reflect.Constructor; @@ -27,7 +28,6 @@ import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.scaffold.InstrumentedType; import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; import net.bytebuddy.implementation.Implementation; @@ -225,9 +225,7 @@ public Constructor load(final OnTimerMethodSpecifier onTimerMethodSpecifier) Class> res = (Class>) unloaded - .load( - findClassLoader(fnClass.getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + .load(findClassLoader(fnClass.getClassLoader()), getClassLoadingStrategy(fnClass)) .getLoaded(); return res; } @@ -277,9 +275,7 @@ public Constructor load(final OnTimerMethodSpecifier onTimerMethodSpecifier) Class> res = (Class>) unloaded - .load( - findClassLoader(fnClass.getClassLoader()), - ClassLoadingStrategy.Default.INJECTION) + .load(findClassLoader(fnClass.getClassLoader()), getClassLoadingStrategy(fnClass)) .getLoaded(); return res; } diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/util/ByteBuddyUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/util/ByteBuddyUtils.java new file mode 100644 index 0000000000000..3a7dd889d9584 --- /dev/null +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/util/ByteBuddyUtils.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.beam.sdk.util; + +import static java.util.Objects.requireNonNull; + +import java.lang.reflect.Method; +import net.bytebuddy.dynamic.loading.ClassInjector; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import org.apache.beam.sdk.util.common.ReflectHelpers; + +/** Utilities for working with Byte Buddy. */ +public final class ByteBuddyUtils { + private ByteBuddyUtils() {} // Non-instantiable + + /** Returns a class loading strategy that is compatible with Java 17+. */ + public static ClassLoadingStrategy getClassLoadingStrategy(Class targetClass) { + try { + ClassLoadingStrategy strategy; + if (ClassInjector.UsingLookup.isAvailable()) { + ClassLoader classLoader = ReflectHelpers.findClassLoader(targetClass); + Class methodHandles = Class.forName("java.lang.invoke.MethodHandles", true, classLoader); + @SuppressWarnings("nullness") // MethodHandles#lookup accepts null + Object lookup = methodHandles.getMethod("lookup").invoke(null); + Class lookupClass = + Class.forName("java.lang.invoke.MethodHandles$Lookup", true, classLoader); + Method privateLookupIn = + methodHandles.getMethod("privateLookupIn", Class.class, lookupClass); + @SuppressWarnings("nullness") // this is a static method, the receiver can be null + Object privateLookup = requireNonNull(privateLookupIn.invoke(null, targetClass, lookup)); + strategy = ClassLoadingStrategy.UsingLookup.of(requireNonNull(privateLookup)); + } else if (ClassInjector.UsingReflection.isAvailable()) { + strategy = ClassLoadingStrategy.Default.INJECTION; + } else { + throw new IllegalStateException("No code generation strategy available"); + } + return strategy; + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("No code generation strategy available", e); + } + } +}