diff --git a/.github/workflows/downstream.yaml b/.github/workflows/downstream.yaml index fa05f8029..b521e571d 100644 --- a/.github/workflows/downstream.yaml +++ b/.github/workflows/downstream.yaml @@ -134,9 +134,11 @@ jobs: - workflows steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v3 with: + distribution: zulu java-version: ${{matrix.java}} - run: java -version + - run: sudo apt-get update -y - run: sudo apt-get install libxml2-utils - run: .kokoro/downstream-client-library-check.sh ${{matrix.repo}} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f163dad2..1007b960e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [2.14.0](https://github.com/googleapis/gax-java/compare/v2.13.0...v2.14.0) (2022-04-01) + + +### Features + +* relocate Netty Native Image configurations from java-core to gax ([#1638](https://github.com/googleapis/gax-java/issues/1638)) ([aafded4](https://github.com/googleapis/gax-java/commit/aafded4a0b779c68d0d5702b722672a0f86ccdd1)) +* relocate protobuf configurations from java-core to gax-java ([#1641](https://github.com/googleapis/gax-java/issues/1641)) ([01d395f](https://github.com/googleapis/gax-java/commit/01d395f486ccd7c364c03ccdfcfbf83b900192c3)) + + +### Bug Fixes + +* update the runtime dependency grpc-java xds to googleapis ([#1643](https://github.com/googleapis/gax-java/issues/1643)) ([b8d9e30](https://github.com/googleapis/gax-java/commit/b8d9e30c3e2209bcd4cc0f808dfdf0a0608aa466)) + ## [2.13.0](https://github.com/googleapis/gax-java/compare/v2.12.2...v2.13.0) (2022-03-25) diff --git a/build.gradle b/build.gradle index 2ff889577..02dde1018 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ plugins { } // TODO: Populate this from dependencies.properties version property (for proper Gradle-Bazel sync) -project.version = "2.13.0" // {x-version-update:gax:current} +project.version = "2.14.0" // {x-version-update:gax:current} allprojects { group = 'com.google.api' @@ -39,7 +39,7 @@ ext { 'maven.io_grpc_grpc_protobuf': "io.grpc:grpc-protobuf:${libraries['version.io_grpc']}", 'maven.io_grpc_grpc_netty_shaded': "io.grpc:grpc-netty-shaded:${libraries['version.io_grpc']}", 'maven.io_grpc_grpc_alts': "io.grpc:grpc-alts:${libraries['version.io_grpc']}", - 'maven.io_grpc_grpc_xds': "io.grpc:grpc-xds:${libraries['version.io_grpc']}", + 'maven.io_grpc_grpc_googleapis': "io.grpc:grpc-googleapis:${libraries['version.io_grpc']}", 'maven.com_google_protobuf': "com.google.protobuf:protobuf-java:${libraries['version.com_google_protobuf']}", 'maven.com_google_protobuf_java_util': "com.google.protobuf:protobuf-java-util:${libraries['version.com_google_protobuf']}") } @@ -322,4 +322,4 @@ sonarqube { property 'sonar.organization', 'googleapis' property 'sonar.host.url', 'https://sonarcloud.io' } -} \ No newline at end of file +} diff --git a/dependencies.properties b/dependencies.properties index d38f94ab2..7e432daa3 100644 --- a/dependencies.properties +++ b/dependencies.properties @@ -8,16 +8,16 @@ # Versions of oneself # {x-version-update-start:gax:current} -version.gax=2.13.0 +version.gax=2.14.0 # {x-version-update-end} # {x-version-update-start:gax:current} -version.gax_grpc=2.13.0 +version.gax_grpc=2.14.0 # {x-version-update-end} # {x-version-update-start:gax:current} -version.gax_bom=2.13.0 +version.gax_bom=2.14.0 # {x-version-update-end} # {x-version-update-start:gax-httpjson:current} -version.gax_httpjson=0.98.0 +version.gax_httpjson=0.99.0 # {x-version-update-end} # Versions for dependencies which actual artifacts differ between Bazel and Gradle. @@ -70,6 +70,8 @@ maven.com_google_http_client_google_http_client=com.google.http-client:google-ht maven.com_google_http_client_google_http_client_gson=com.google.http-client:google-http-client-gson:1.41.5 maven.org_codehaus_mojo_animal_sniffer_annotations=org.codehaus.mojo:animal-sniffer-annotations:1.18 maven.javax_annotation_javax_annotation_api=javax.annotation:javax.annotation-api:1.3.2 +maven.org_graalvm_nativeimage_svm=org.graalvm.nativeimage:svm:22.0.0.2 +maven.org_graalvm_sdk=org.graalvm.sdk:graal-sdk:22.0.0.2 # Testing maven artifacts maven.junit_junit=junit:junit:4.13.2 diff --git a/gax-bom/build.gradle b/gax-bom/build.gradle index 49d8016aa..1dbcded2d 100644 --- a/gax-bom/build.gradle +++ b/gax-bom/build.gradle @@ -5,7 +5,7 @@ plugins { archivesBaseName = 'gax-bom' -project.version = "2.13.0" // {x-version-update:gax-bom:current} +project.version = "2.14.0" // {x-version-update:gax-bom:current} def mavenJavaDir = "$buildDir/publications/mavenJava" def mavenJavaBomOutputFile = file(mavenJavaDir + '/pom-default.xml') diff --git a/gax-bom/pom.xml b/gax-bom/pom.xml index cb10a67db..e861f615c 100644 --- a/gax-bom/pom.xml +++ b/gax-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.api gax-bom - 2.13.0 + 2.14.0 pom GAX (Google Api eXtensions) for Java Google Api eXtensions for Java @@ -33,34 +33,34 @@ com.google.api gax - 2.13.0 + 2.14.0 com.google.api gax - 2.13.0 + 2.14.0 testlib com.google.api gax-grpc - 2.13.0 + 2.14.0 com.google.api gax-grpc - 2.13.0 + 2.14.0 testlib com.google.api gax-httpjson - 0.98.0 + 0.99.0 com.google.api gax-httpjson - 0.98.0 + 0.99.0 testlib diff --git a/gax-grpc/BUILD.bazel b/gax-grpc/BUILD.bazel index 3a177f4ab..5ae856c4f 100644 --- a/gax-grpc/BUILD.bazel +++ b/gax-grpc/BUILD.bazel @@ -31,6 +31,8 @@ _COMPILE_DEPS = [ "@io_netty_netty_tcnative_boringssl_static//jar", "@javax_annotation_javax_annotation_api//jar", "//gax:gax", + "@org_graalvm_nativeimage_svm//jar", + "@org_graalvm_sdk//jar" ] _TEST_COMPILE_DEPS = [ diff --git a/gax-grpc/build.gradle b/gax-grpc/build.gradle index 84d3464cd..a8d3df04b 100644 --- a/gax-grpc/build.gradle +++ b/gax-grpc/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = 'gax-grpc' // TODO: Populate this from dependencies.properties version property (for proper Gradle-Bazel sync) -project.version = "2.13.0" // {x-version-update:gax-grpc:current} +project.version = "2.14.0" // {x-version-update:gax-grpc:current} dependencies { api(project(':gax'), @@ -20,9 +20,11 @@ dependencies { libraries['maven.io_grpc_grpc_protobuf'], libraries['maven.io_grpc_grpc_stub']) - runtimeOnly libraries['maven.io_grpc_grpc_xds'] + runtimeOnly libraries['maven.io_grpc_grpc_googleapis'] - compileOnly libraries['maven.com_google_auto_value_auto_value'] + compileOnly(libraries['maven.com_google_auto_value_auto_value'], + libraries['maven.org_graalvm_sdk'], + libraries['maven.org_graalvm_nativeimage_svm']) testImplementation( project(':gax').sourceSets.test.output, libraries['maven.junit_junit'], diff --git a/gax-grpc/src/main/java/com/google/api/gax/grpc/nativeimage/GrpcNettyFeature.java b/gax-grpc/src/main/java/com/google/api/gax/grpc/nativeimage/GrpcNettyFeature.java new file mode 100644 index 000000000..1e1167e09 --- /dev/null +++ b/gax-grpc/src/main/java/com/google/api/gax/grpc/nativeimage/GrpcNettyFeature.java @@ -0,0 +1,132 @@ +/* + * Copyright 2022 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.grpc.nativeimage; + +import static com.google.api.gax.nativeimage.NativeImageUtils.registerClassForReflection; +import static com.google.api.gax.nativeimage.NativeImageUtils.registerClassHierarchyForReflection; +import static com.google.api.gax.nativeimage.NativeImageUtils.registerForReflectiveInstantiation; +import static com.google.api.gax.nativeimage.NativeImageUtils.registerForUnsafeFieldAccess; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import org.graalvm.nativeimage.hosted.Feature; + +/** Configures Native Image settings for the grpc-netty-shaded dependency. */ +@AutomaticFeature +final class GrpcNettyFeature implements Feature { + + private static final String GRPC_NETTY_SHADED_CLASS = + "io.grpc.netty.shaded.io.grpc.netty.NettyServer"; + + private static final String GOOGLE_AUTH_CLASS = + "com.google.auth.oauth2.ServiceAccountCredentials"; + + private static final String NETTY_SHADED_PACKAGE = + "io.grpc.netty.shaded.io.netty.util.internal.shaded."; + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + loadGoogleAuthClasses(access); + loadGrpcNettyClasses(access); + loadMiscClasses(access); + } + + private static void loadGoogleAuthClasses(BeforeAnalysisAccess access) { + // For com.google.auth:google-auth-library-oauth2-http + Class authClass = access.findClassByName(GOOGLE_AUTH_CLASS); + if (authClass != null) { + registerClassHierarchyForReflection(access, GOOGLE_AUTH_CLASS); + registerClassHierarchyForReflection( + access, "com.google.auth.oauth2.ServiceAccountJwtAccessCredentials"); + } + } + + private static void loadGrpcNettyClasses(BeforeAnalysisAccess access) { + // For io.grpc:grpc-netty-shaded + Class nettyShadedClass = access.findClassByName(GRPC_NETTY_SHADED_CLASS); + if (nettyShadedClass != null) { + // Misc. classes used by grpc-netty-shaded + registerForReflectiveInstantiation( + access, "io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel"); + registerClassForReflection( + access, "io.grpc.netty.shaded.io.netty.util.internal.NativeLibraryUtil"); + registerClassForReflection(access, "io.grpc.netty.shaded.io.netty.util.ReferenceCountUtil"); + registerClassForReflection( + access, "io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator"); + + // Epoll Libraries + registerClassForReflection(access, "io.grpc.netty.shaded.io.netty.channel.epoll.Epoll"); + registerClassForReflection( + access, "io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption"); + registerClassForReflection( + access, "io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup"); + registerForReflectiveInstantiation( + access, "io.grpc.netty.shaded.io.netty.channel.epoll.EpollServerSocketChannel"); + registerForReflectiveInstantiation( + access, "io.grpc.netty.shaded.io.netty.channel.epoll.EpollSocketChannel"); + + // Unsafe field accesses + registerForUnsafeFieldAccess( + access, + NETTY_SHADED_PACKAGE + "org.jctools.queues.MpscArrayQueueProducerIndexField", + "producerIndex"); + registerForUnsafeFieldAccess( + access, + NETTY_SHADED_PACKAGE + "org.jctools.queues.MpscArrayQueueProducerLimitField", + "producerLimit"); + registerForUnsafeFieldAccess( + access, + NETTY_SHADED_PACKAGE + "org.jctools.queues.MpscArrayQueueConsumerIndexField", + "consumerIndex"); + registerForUnsafeFieldAccess( + access, + NETTY_SHADED_PACKAGE + "org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields", + "producerIndex"); + registerForUnsafeFieldAccess( + access, + NETTY_SHADED_PACKAGE + "org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields", + "producerLimit"); + registerForUnsafeFieldAccess( + access, + NETTY_SHADED_PACKAGE + "org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields", + "consumerIndex"); + } + } + + /** Miscellaneous classes that need to be registered coming from various JARs. */ + private static void loadMiscClasses(BeforeAnalysisAccess access) { + registerClassHierarchyForReflection(access, "com.google.protobuf.DescriptorProtos"); + registerClassForReflection(access, "com.google.api.FieldBehavior"); + + registerForUnsafeFieldAccess(access, "javax.net.ssl.SSLContext", "contextSpi"); + registerClassForReflection(access, "java.lang.management.ManagementFactory"); + registerClassForReflection(access, "java.lang.management.RuntimeMXBean"); + } +} diff --git a/gax-grpc/src/main/java/com/google/api/gax/grpc/nativeimage/ProtobufMessageFeature.java b/gax-grpc/src/main/java/com/google/api/gax/grpc/nativeimage/ProtobufMessageFeature.java new file mode 100644 index 000000000..0b65e8c7f --- /dev/null +++ b/gax-grpc/src/main/java/com/google/api/gax/grpc/nativeimage/ProtobufMessageFeature.java @@ -0,0 +1,119 @@ +/* + * Copyright 2022 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.grpc.nativeimage; + +import com.google.api.gax.nativeimage.NativeImageUtils; +import com.oracle.svm.core.annotate.AutomaticFeature; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.RuntimeReflection; + +/** + * A optional feature which registers reflective usages of the GRPC Protobuf libraries. + * + *

This feature is only needed if you need to access proto objects reflectively (such as + * printing/logging proto objects). + */ +@AutomaticFeature +final class ProtobufMessageFeature implements Feature { + + // Proto classes to check on the classpath. + private static final String PROTO_MESSAGE_CLASS = "com.google.protobuf.GeneratedMessageV3"; + private static final String PROTO_ENUM_CLASS = "com.google.protobuf.ProtocolMessageEnum"; + private static final String ENUM_VAL_DESCRIPTOR_CLASS = + "com.google.protobuf.Descriptors$EnumValueDescriptor"; + + // Prefixes of methods accessed reflectively by + // com.google.protobuf.GeneratedMessageV3$ReflectionInvoker + private static final List METHOD_ACCESSOR_PREFIXES = + Arrays.asList("get", "set", "has", "add", "clear", "newBuilder"); + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + Class protoMessageClass = access.findClassByName(PROTO_MESSAGE_CLASS); + if (protoMessageClass != null) { + Method internalAccessorMethod = + NativeImageUtils.getMethodOrFail(protoMessageClass, "internalGetFieldAccessorTable"); + + // Finds every class whose `internalGetFieldAccessorTable()` is reached and registers it. + // `internalGetFieldAccessorTable()` is used downstream to access the class reflectively. + access.registerMethodOverrideReachabilityHandler( + (duringAccess, method) -> { + registerFieldAccessors(method.getDeclaringClass()); + registerFieldAccessors(getBuilderClass(method.getDeclaringClass())); + }, + internalAccessorMethod); + } + + Class protoEnumClass = access.findClassByName(PROTO_ENUM_CLASS); + if (protoEnumClass != null) { + // Finds every reachable proto enum class and registers specific methods for reflection. + access.registerSubtypeReachabilityHandler( + (duringAccess, subtypeClass) -> { + if (!PROTO_ENUM_CLASS.equals(subtypeClass.getName())) { + Method method = + NativeImageUtils.getMethodOrFail( + subtypeClass, + "valueOf", + duringAccess.findClassByName(ENUM_VAL_DESCRIPTOR_CLASS)); + RuntimeReflection.register(method); + + method = NativeImageUtils.getMethodOrFail(subtypeClass, "getValueDescriptor"); + RuntimeReflection.register(method); + } + }, + protoEnumClass); + } + } + + /** Given a proto class, registers the public accessor methods for the provided proto class. */ + private static void registerFieldAccessors(Class protoClass) { + for (Method method : protoClass.getMethods()) { + boolean hasAccessorPrefix = + METHOD_ACCESSOR_PREFIXES.stream().anyMatch(prefix -> method.getName().startsWith(prefix)); + if (hasAccessorPrefix) { + RuntimeReflection.register(method); + } + } + } + + /** Given a proto class, returns the Builder nested class. */ + private static Class getBuilderClass(Class protoClass) { + for (Class clazz : protoClass.getClasses()) { + if (clazz.getName().endsWith("Builder")) { + return clazz; + } + } + return null; + } +} diff --git a/gax-httpjson/build.gradle b/gax-httpjson/build.gradle index c80ebfc73..b52dee58f 100644 --- a/gax-httpjson/build.gradle +++ b/gax-httpjson/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = 'gax-httpjson' // TODO: Populate this from dependencies.properties version property (for proper Gradle-Bazel sync) -project.version = "0.98.0" // {x-version-update:gax-httpjson:current} +project.version = "0.99.0" // {x-version-update:gax-httpjson:current} dependencies { api(project(':gax'), diff --git a/gax/BUILD.bazel b/gax/BUILD.bazel index b9dd9a067..f35f3fc99 100644 --- a/gax/BUILD.bazel +++ b/gax/BUILD.bazel @@ -24,6 +24,8 @@ _COMPILE_DEPS = [ "@com_google_code_gson_gson//jar", "@com_google_guava_failureaccess//jar", "@javax_annotation_javax_annotation_api//jar", + "@org_graalvm_nativeimage_svm//jar", + "@org_graalvm_sdk//jar" ] _TEST_COMPILE_DEPS = [ diff --git a/gax/build.gradle b/gax/build.gradle index fc6fbb545..cdd5d63bd 100644 --- a/gax/build.gradle +++ b/gax/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = "gax" // TODO: Populate this from dependencies.properties version property (for proper Gradle-Bazel sync) -project.version = "2.13.0" // {x-version-update:gax:current} +project.version = "2.14.0" // {x-version-update:gax:current} dependencies { api(libraries['maven.com_google_api_api_common'], @@ -13,7 +13,9 @@ dependencies { libraries['maven.com_google_guava_guava'], libraries['maven.io_opencensus_opencensus_api']) - compileOnly libraries['maven.com_google_auto_value_auto_value'] + compileOnly(libraries['maven.com_google_auto_value_auto_value'], + libraries['maven.org_graalvm_sdk'], + libraries['maven.org_graalvm_nativeimage_svm']) testImplementation(libraries['maven.junit_junit'], libraries['maven.org_mockito_mockito_core'], diff --git a/gax/src/main/java/com/google/api/gax/nativeimage/NativeImageUtils.java b/gax/src/main/java/com/google/api/gax/nativeimage/NativeImageUtils.java new file mode 100644 index 000000000..603d26e3c --- /dev/null +++ b/gax/src/main/java/com/google/api/gax/nativeimage/NativeImageUtils.java @@ -0,0 +1,140 @@ +/* + * Copyright 2022 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.nativeimage; + +import com.google.api.core.InternalApi; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.graalvm.nativeimage.hosted.Feature.FeatureAccess; +import org.graalvm.nativeimage.hosted.RuntimeReflection; + +/** Internal class offering helper methods for registering methods/classes for reflection. */ +@InternalApi +public class NativeImageUtils { + + private static final Logger LOGGER = Logger.getLogger(NativeImageUtils.class.getName()); + private static final String CLASS_REFLECTION_ERROR_MESSAGE = + "Failed to find {0} on the classpath for reflection."; + + private NativeImageUtils() {} + + /** Returns the method of a class or fails if it is not present. */ + public static Method getMethodOrFail(Class clazz, String methodName, Class... params) { + try { + return clazz.getDeclaredMethod(methodName, params); + } catch (NoSuchMethodException e) { + throw new IllegalStateException( + String.format("Failed to find method %s for class %s", methodName, clazz.getName()), e); + } + } + + /** Registers a class for reflective construction via its default constructor. */ + public static void registerForReflectiveInstantiation(FeatureAccess access, String className) { + Class clazz = access.findClassByName(className); + if (clazz != null) { + RuntimeReflection.register(clazz); + RuntimeReflection.registerForReflectiveInstantiation(clazz); + } else { + LOGGER.log( + Level.WARNING, + "Failed to find {0} on the classpath for reflective instantiation.", + className); + } + } + + /** Registers all constructors of a class for reflection. */ + public static void registerConstructorsForReflection(FeatureAccess access, String name) { + Class clazz = access.findClassByName(name); + if (clazz != null) { + RuntimeReflection.register(clazz); + RuntimeReflection.register(clazz.getDeclaredConstructors()); + } else { + LOGGER.log(Level.WARNING, CLASS_REFLECTION_ERROR_MESSAGE, name); + } + } + + /** Registers an entire class for reflection use. */ + public static void registerClassForReflection(FeatureAccess access, String name) { + Class clazz = access.findClassByName(name); + if (clazz != null) { + RuntimeReflection.register(clazz); + RuntimeReflection.register(clazz.getDeclaredConstructors()); + RuntimeReflection.register(clazz.getDeclaredFields()); + RuntimeReflection.register(clazz.getDeclaredMethods()); + } else { + LOGGER.log(Level.WARNING, CLASS_REFLECTION_ERROR_MESSAGE, name); + } + } + + /** + * Registers the transitive class hierarchy of the provided {@code className} for reflection. + * + *

The transitive class hierarchy contains the class itself and its transitive set of + * *non-private* nested subclasses. + */ + public static void registerClassHierarchyForReflection(FeatureAccess access, String className) { + Class clazz = access.findClassByName(className); + if (clazz != null) { + registerClassForReflection(access, className); + for (Class nestedClass : clazz.getDeclaredClasses()) { + if (!Modifier.isPrivate(nestedClass.getModifiers())) { + registerClassHierarchyForReflection(access, nestedClass.getName()); + } + } + } else { + LOGGER.log(Level.WARNING, CLASS_REFLECTION_ERROR_MESSAGE, className); + } + } + + /** Registers a class for unsafe reflective field access. */ + public static void registerForUnsafeFieldAccess( + FeatureAccess access, String className, String... fields) { + Class clazz = access.findClassByName(className); + if (clazz != null) { + RuntimeReflection.register(clazz); + for (String fieldName : fields) { + try { + RuntimeReflection.register(clazz.getDeclaredField(fieldName)); + } catch (NoSuchFieldException ex) { + LOGGER.warning("Failed to register field " + fieldName + " for class " + className); + LOGGER.warning(ex.getMessage()); + } + } + } else { + LOGGER.log( + Level.WARNING, + "Failed to find {0} on the classpath for unsafe fields access registration.", + className); + } + } +} diff --git a/versions.txt b/versions.txt index 53e849d63..41301da08 100644 --- a/versions.txt +++ b/versions.txt @@ -1,7 +1,7 @@ # Format: # module:released-version:current-version -gax:2.13.0:2.13.0 -gax-bom:2.13.0:2.13.0 -gax-grpc:2.13.0:2.13.0 -gax-httpjson:0.98.0:0.98.0 +gax:2.14.0:2.14.0 +gax-bom:2.14.0:2.14.0 +gax-grpc:2.14.0:2.14.0 +gax-httpjson:0.99.0:0.99.0