From 14a34ad5ee136e9f7645e3d006761f52314f4119 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:41:40 -0400 Subject: [PATCH 01/12] update IT tests to ensure that a protobuf version is always getting sent --- .../java/com/google/showcase/v1beta1/it/ITVersionHeaders.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITVersionHeaders.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITVersionHeaders.java index 09255fe278..303cab98ec 100644 --- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITVersionHeaders.java +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITVersionHeaders.java @@ -241,7 +241,7 @@ void testHttpJsonCompliance_userApiVersionSetSuccess() throws IOException { @Test void testGrpcCall_sendsCorrectApiClientHeader() { Pattern defautlGrpcHeaderPattern = - Pattern.compile("gl-java/.* gapic/.*?--protobuf-.* gax/.* grpc/.* protobuf/.*"); + Pattern.compile("gl-java/.* gapic/.*?--protobuf-\\d.* gax/.* grpc/.* protobuf/\\d.*"); grpcClient.echo(EchoRequest.newBuilder().build()); String headerValue = grpcInterceptor.metadata.get(API_CLIENT_HEADER_KEY); assertTrue(defautlGrpcHeaderPattern.matcher(headerValue).matches()); @@ -250,7 +250,7 @@ void testGrpcCall_sendsCorrectApiClientHeader() { @Test void testHttpJson_sendsCorrectApiClientHeader() { Pattern defautlHttpHeaderPattern = - Pattern.compile("gl-java/.* gapic/.*?--protobuf-.* gax/.* rest/ protobuf/.*"); + Pattern.compile("gl-java/.* gapic/.*?--protobuf-\\d.* gax/.* rest/ protobuf/\\d.*"); httpJsonClient.echo(EchoRequest.newBuilder().build()); ArrayList headerValues = (ArrayList) From 703760212ec557935c2aabe12638208c3988d7e6 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:12:39 -0500 Subject: [PATCH 02/12] Updated logic for retrieving runtime protobuf for a more verbose solution --- .../google/api/gax/core/GaxProperties.java | 31 ++++++++- .../api/gax/util/ClassLoaderWrapper.java | 15 +++++ .../api/gax/util/IClassLoaderWrapper.java | 14 ++++ .../com.google.api/gax/reflect-config.json | 10 +++ .../api/gax/core/GaxPropertiesTest.java | 67 ++++++++++++++++--- 5 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java create mode 100644 gax-java/gax/src/main/resources/META-INF/native-image/com.google.api/gax/reflect-config.json diff --git a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java index f15046afcb..0908cb1e68 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java @@ -30,6 +30,7 @@ package com.google.api.gax.core; import com.google.api.core.InternalApi; +import com.google.api.gax.util.*; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.protobuf.Any; @@ -49,7 +50,7 @@ public class GaxProperties { private static final String GAX_VERSION = getLibraryVersion(GaxProperties.class, "version.gax"); private static final String JAVA_VERSION = getRuntimeVersion(); private static final String PROTOBUF_VERSION = - getBundleVersion(Any.class).orElse(DEFAULT_VERSION); + getProtobufVersion(new ClassLoaderWrapper(), Any.class); private GaxProperties() {} @@ -144,8 +145,34 @@ static Optional getBundleVersion(Class clazz) { return Optional.ofNullable(attributes.getValue("Bundle-Version")); } } catch (Exception e) { - // Unable to read Bundle-Version from manifest. Recover gracefully. return Optional.empty(); } } + + /** + * Returns the current Protobuf runtime version as reported by com.google.protobuf.RuntimeVersion + * if available, otherwise by reading from MANIFEST file, and if not available defaulting to + * generic protobuf 3 as RuntimeVersion class is available in protobuf version 4+ + */ + @VisibleForTesting + static String getProtobufVersion(IClassLoaderWrapper classLoader, Class clazz) { + try { + Class protobufRuntimeVersionClass = + classLoader.loadClass("com.google.protobuf.RuntimeVersion"); + return classLoader.getFieldValue(protobufRuntimeVersionClass, "MAJOR") + + "." + + classLoader.getFieldValue(protobufRuntimeVersionClass, "MINOR") + + "." + + classLoader.getFieldValue(protobufRuntimeVersionClass, "PATCH"); + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + Optional protobufVersionFromManifest = getBundleVersion(clazz); + if (protobufVersionFromManifest.isPresent()) { + return protobufVersionFromManifest.get(); + } else { + // If manifest file is not available default to generic 3 as we know RuntimeVersion class is + // available in protobuf jar 4+. + return "3"; + } + } + } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java new file mode 100644 index 0000000000..0f3677318e --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java @@ -0,0 +1,15 @@ +package com.google.api.gax.util; + +/* Wrapper class for Final Class methods that can not be mocked */ +public class ClassLoaderWrapper implements IClassLoaderWrapper { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return Class.forName(name); + } + + @Override + public Object getFieldValue(Class clazz, String fieldName) + throws NoSuchFieldException, IllegalAccessException { + return clazz.getField(fieldName).get(null); + } +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java new file mode 100644 index 0000000000..22ca68e337 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java @@ -0,0 +1,14 @@ +package com.google.api.gax.util; + +/* Interface that allows for unit testing reflection logic. */ +public interface IClassLoaderWrapper { + /* Wraps @java.lang.Class#loadClass method */ + Class loadClass(String name) throws ClassNotFoundException; + + /* + * Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field + * Expected field is of type int. + */ + Object getFieldValue(Class clazz, String fieldName) + throws NoSuchFieldException, IllegalAccessException; +} diff --git a/gax-java/gax/src/main/resources/META-INF/native-image/com.google.api/gax/reflect-config.json b/gax-java/gax/src/main/resources/META-INF/native-image/com.google.api/gax/reflect-config.json new file mode 100644 index 0000000000..3a38d53361 --- /dev/null +++ b/gax-java/gax/src/main/resources/META-INF/native-image/com.google.api/gax/reflect-config.json @@ -0,0 +1,10 @@ +[ + { + "name": "com.google.protobuf.RuntimeVersion", + "fields" : [ + { "name" : "MAJOR" }, + { "name" : "MINOR" }, + { "name" : "PATCH" } + ] + } +] \ No newline at end of file diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index 1369ec35ae..081a1472db 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -30,11 +30,14 @@ package com.google.api.gax.core; import static com.google.api.gax.core.GaxProperties.getBundleVersion; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import com.google.api.gax.util.*; import com.google.common.base.Strings; +import com.google.protobuf.*; import java.io.IOException; import java.util.Optional; import java.util.regex.Pattern; @@ -160,12 +163,8 @@ void testGetJavaRuntimeInfo_nullJavaVersion() { @Test public void testGetProtobufVersion() throws IOException { - Version version = readVersion(GaxProperties.getProtobufVersion()); - - assertTrue(version.major >= 3); - if (version.major == 3) { - assertTrue(version.minor >= 25); - } + assertTrue( + Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(GaxProperties.getProtobufVersion()).find()); } @Test @@ -175,6 +174,56 @@ public void testGetBundleVersion_noManifestFile() throws IOException { assertFalse(version.isPresent()); } + @Test + void testGetProtobufVersion_success() throws Exception { + IClassLoaderWrapper mockClassLoader = + mock(IClassLoaderWrapper.class); + when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) + .thenAnswer(invocationOnMock -> Class.class); + when(mockClassLoader.getFieldValue(Class.class, "MAJOR")).thenReturn("2"); + when(mockClassLoader.getFieldValue(Class.class, "MINOR")).thenReturn("3"); + when(mockClassLoader.getFieldValue(Class.class, "PATCH")).thenReturn("4"); + + String version = GaxProperties.getProtobufVersion(mockClassLoader, Any.class); + + assertEquals("2.3.4", version); + } + + @Test + void testGetProtobufVersion_classNotFoundException() throws Exception { + IClassLoaderWrapper mockClassLoader = + mock(IClassLoaderWrapper.class); + when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) + .thenThrow(new ClassNotFoundException("")); + + String version = GaxProperties.getProtobufVersion(mockClassLoader, Any.class); + + assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); + } + + @Test + void testgetProtobufVersion_noSuchFieldException() throws Exception { + IClassLoaderWrapper mockClassLoader = + mock(IClassLoaderWrapper.class); + when(mockClassLoader.getFieldValue(any(), any())).thenThrow(NoSuchFieldException.class); + + String version = GaxProperties.getProtobufVersion(mockClassLoader, Any.class); + + assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); + } + + @Test + void testGetProtobufVersion_noManifest() throws Exception { + IClassLoaderWrapper mockClassLoader = + mock(IClassLoaderWrapper.class); + when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) + .thenThrow(new ClassNotFoundException("")); + + String version = GaxProperties.getProtobufVersion(mockClassLoader, GaxProperties.class); + + assertEquals("3", version); + } + private Version readVersion(String version) { assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); String[] versionComponents = version.split("\\."); From 426963bf69ba19a3e34e9afa3233b0623c384889 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:18:38 -0500 Subject: [PATCH 03/12] formatting fixes --- .../api/gax/rpc/ApiClientHeaderProvider.java | 2 +- .../api/gax/util/ClassLoaderWrapper.java | 47 +++++++++++++++---- .../api/gax/util/IClassLoaderWrapper.java | 45 ++++++++++++++---- .../api/gax/core/GaxPropertiesTest.java | 12 ++--- 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiClientHeaderProvider.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiClientHeaderProvider.java index 35307764d2..52f923e111 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiClientHeaderProvider.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiClientHeaderProvider.java @@ -88,7 +88,7 @@ private static String checkAndAppendProtobufVersionIfNecessary( // TODO(b/366417603): appending protobuf version to existing client library token until resolved Pattern pattern = Pattern.compile("(gccl|gapic)\\S*"); Matcher matcher = pattern.matcher(apiClientHeaderValue); - if (matcher.find()) { + if (matcher.find() && GaxProperties.getProtobufVersion() != null) { return apiClientHeaderValue.substring(0, matcher.end()) + "--" + PROTOBUF_HEADER_VERSION_KEY diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java index 0f3677318e..9163722968 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java @@ -1,15 +1,44 @@ +/* + * Copyright 2024 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.util; /* Wrapper class for Final Class methods that can not be mocked */ public class ClassLoaderWrapper implements IClassLoaderWrapper { - @Override - public Class loadClass(String name) throws ClassNotFoundException { - return Class.forName(name); - } + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return Class.forName(name); + } - @Override - public Object getFieldValue(Class clazz, String fieldName) - throws NoSuchFieldException, IllegalAccessException { - return clazz.getField(fieldName).get(null); - } + @Override + public Object getFieldValue(Class clazz, String fieldName) + throws NoSuchFieldException, IllegalAccessException { + return clazz.getField(fieldName).get(null); + } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java index 22ca68e337..6f3807b212 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java @@ -1,14 +1,43 @@ +/* + * Copyright 2024 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.util; /* Interface that allows for unit testing reflection logic. */ public interface IClassLoaderWrapper { - /* Wraps @java.lang.Class#loadClass method */ - Class loadClass(String name) throws ClassNotFoundException; + /* Wraps @java.lang.Class#loadClass method */ + Class loadClass(String name) throws ClassNotFoundException; - /* - * Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field - * Expected field is of type int. - */ - Object getFieldValue(Class clazz, String fieldName) - throws NoSuchFieldException, IllegalAccessException; + /* + * Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field + * Expected field is of type int. + */ + Object getFieldValue(Class clazz, String fieldName) + throws NoSuchFieldException, IllegalAccessException; } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index 081a1472db..437b6b553f 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -176,8 +176,7 @@ public void testGetBundleVersion_noManifestFile() throws IOException { @Test void testGetProtobufVersion_success() throws Exception { - IClassLoaderWrapper mockClassLoader = - mock(IClassLoaderWrapper.class); + IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) .thenAnswer(invocationOnMock -> Class.class); when(mockClassLoader.getFieldValue(Class.class, "MAJOR")).thenReturn("2"); @@ -191,8 +190,7 @@ void testGetProtobufVersion_success() throws Exception { @Test void testGetProtobufVersion_classNotFoundException() throws Exception { - IClassLoaderWrapper mockClassLoader = - mock(IClassLoaderWrapper.class); + IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) .thenThrow(new ClassNotFoundException("")); @@ -203,8 +201,7 @@ void testGetProtobufVersion_classNotFoundException() throws Exception { @Test void testgetProtobufVersion_noSuchFieldException() throws Exception { - IClassLoaderWrapper mockClassLoader = - mock(IClassLoaderWrapper.class); + IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); when(mockClassLoader.getFieldValue(any(), any())).thenThrow(NoSuchFieldException.class); String version = GaxProperties.getProtobufVersion(mockClassLoader, Any.class); @@ -214,8 +211,7 @@ void testgetProtobufVersion_noSuchFieldException() throws Exception { @Test void testGetProtobufVersion_noManifest() throws Exception { - IClassLoaderWrapper mockClassLoader = - mock(IClassLoaderWrapper.class); + IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) .thenThrow(new ClassNotFoundException("")); From 61855d05d521234952287c266128c001b1fb121c Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Mon, 4 Nov 2024 19:24:36 -0500 Subject: [PATCH 04/12] cleanup --- .../java/com/google/api/gax/core/GaxProperties.java | 12 +++++++----- .../com/google/api/gax/util/ClassLoaderWrapper.java | 2 +- .../com/google/api/gax/util/IClassLoaderWrapper.java | 7 ++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java index 0908cb1e68..90ddc80894 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java @@ -30,7 +30,8 @@ package com.google.api.gax.core; import com.google.api.core.InternalApi; -import com.google.api.gax.util.*; +import com.google.api.gax.util.ClassLoaderWrapper; +import com.google.api.gax.util.IClassLoaderWrapper; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.protobuf.Any; @@ -145,14 +146,15 @@ static Optional getBundleVersion(Class clazz) { return Optional.ofNullable(attributes.getValue("Bundle-Version")); } } catch (Exception e) { + // Unable to read Bundle-Version from manifest. Recover gracefully. return Optional.empty(); } } /** - * Returns the current Protobuf runtime version as reported by com.google.protobuf.RuntimeVersion - * if available, otherwise by reading from MANIFEST file, and if not available defaulting to - * generic protobuf 3 as RuntimeVersion class is available in protobuf version 4+ + * Returns the Protobuf runtime version as reported by com.google.protobuf.RuntimeVersion, + * if class is available, otherwise by reading from MANIFEST file. If niether option is available defaults to + * protobuf version 3 as RuntimeVersion class is available in protobuf version 4+ */ @VisibleForTesting static String getProtobufVersion(IClassLoaderWrapper classLoader, Class clazz) { @@ -169,7 +171,7 @@ static String getProtobufVersion(IClassLoaderWrapper classLoader, Class clazz) { if (protobufVersionFromManifest.isPresent()) { return protobufVersionFromManifest.get(); } else { - // If manifest file is not available default to generic 3 as we know RuntimeVersion class is + // If manifest file is not available default to protobuf generic version 3 as we know RuntimeVersion class is // available in protobuf jar 4+. return "3"; } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java index 9163722968..60637ac22d 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java @@ -29,7 +29,7 @@ */ package com.google.api.gax.util; -/* Wrapper class for Final Class methods that can not be mocked */ +/* Wrapper class for reflection Class methods to enable unit testing. */ public class ClassLoaderWrapper implements IClassLoaderWrapper { @Override public Class loadClass(String name) throws ClassNotFoundException { diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java index 6f3807b212..632190b08c 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java @@ -31,13 +31,10 @@ /* Interface that allows for unit testing reflection logic. */ public interface IClassLoaderWrapper { - /* Wraps @java.lang.Class#loadClass method */ + /* Wraps {@link java.lang.Class#loadClass} method */ Class loadClass(String name) throws ClassNotFoundException; - /* - * Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field - * Expected field is of type int. - */ + /* Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field */ Object getFieldValue(Class clazz, String fieldName) throws NoSuchFieldException, IllegalAccessException; } From d1f7a5e5c2d61593796356d9b435f9ba2e615266 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:46:34 -0500 Subject: [PATCH 05/12] cleaned up naming convention/unnecessary interface for ClassWrapper --- .../google/api/gax/core/GaxProperties.java | 28 +++++-------- ...ssLoaderWrapper.java => ClassWrapper.java} | 9 +++-- .../api/gax/util/IClassLoaderWrapper.java | 40 ------------------- .../api/gax/core/GaxPropertiesTest.java | 30 +++++++------- 4 files changed, 31 insertions(+), 76 deletions(-) rename gax-java/gax/src/main/java/com/google/api/gax/util/{ClassLoaderWrapper.java => ClassWrapper.java} (87%) delete mode 100644 gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java diff --git a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java index 90ddc80894..7d42eca440 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java @@ -30,8 +30,7 @@ package com.google.api.gax.core; import com.google.api.core.InternalApi; -import com.google.api.gax.util.ClassLoaderWrapper; -import com.google.api.gax.util.IClassLoaderWrapper; +import com.google.api.gax.util.ClassWrapper; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.protobuf.Any; @@ -51,7 +50,7 @@ public class GaxProperties { private static final String GAX_VERSION = getLibraryVersion(GaxProperties.class, "version.gax"); private static final String JAVA_VERSION = getRuntimeVersion(); private static final String PROTOBUF_VERSION = - getProtobufVersion(new ClassLoaderWrapper(), Any.class); + getProtobufVersion(new ClassWrapper(), Any.class); private GaxProperties() {} @@ -157,24 +156,19 @@ static Optional getBundleVersion(Class clazz) { * protobuf version 3 as RuntimeVersion class is available in protobuf version 4+ */ @VisibleForTesting - static String getProtobufVersion(IClassLoaderWrapper classLoader, Class clazz) { + static String getProtobufVersion(ClassWrapper classWrapper, Class clazz) { try { Class protobufRuntimeVersionClass = - classLoader.loadClass("com.google.protobuf.RuntimeVersion"); - return classLoader.getFieldValue(protobufRuntimeVersionClass, "MAJOR") + classWrapper.forName("com.google.protobuf.RuntimeVersion"); + return classWrapper.getFieldValue(protobufRuntimeVersionClass, "MAJOR") + "." - + classLoader.getFieldValue(protobufRuntimeVersionClass, "MINOR") + + classWrapper.getFieldValue(protobufRuntimeVersionClass, "MINOR") + "." - + classLoader.getFieldValue(protobufRuntimeVersionClass, "PATCH"); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { - Optional protobufVersionFromManifest = getBundleVersion(clazz); - if (protobufVersionFromManifest.isPresent()) { - return protobufVersionFromManifest.get(); - } else { - // If manifest file is not available default to protobuf generic version 3 as we know RuntimeVersion class is - // available in protobuf jar 4+. - return "3"; - } + + classWrapper.getFieldValue(protobufRuntimeVersionClass, "PATCH"); + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | SecurityException e) { + // If manifest file is not available default to protobuf generic version 3 as we know RuntimeVersion class is + // available in protobuf jar 4+. + return getBundleVersion(clazz).orElse("3"); } } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java similarity index 87% rename from gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java rename to gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java index 60637ac22d..945934bf03 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassLoaderWrapper.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java @@ -30,13 +30,14 @@ package com.google.api.gax.util; /* Wrapper class for reflection Class methods to enable unit testing. */ -public class ClassLoaderWrapper implements IClassLoaderWrapper { - @Override - public Class loadClass(String name) throws ClassNotFoundException { +public class ClassWrapper { + + /* Wraps {@link java.lang.Class#forName} method */ + public Class forName(String name) throws ClassNotFoundException { return Class.forName(name); } - @Override + /* Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field */ public Object getFieldValue(Class clazz, String fieldName) throws NoSuchFieldException, IllegalAccessException { return clazz.getField(fieldName).get(null); diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java deleted file mode 100644 index 632190b08c..0000000000 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/IClassLoaderWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 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.util; - -/* Interface that allows for unit testing reflection logic. */ -public interface IClassLoaderWrapper { - /* Wraps {@link java.lang.Class#loadClass} method */ - Class loadClass(String name) throws ClassNotFoundException; - - /* Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field */ - Object getFieldValue(Class clazz, String fieldName) - throws NoSuchFieldException, IllegalAccessException; -} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index 437b6b553f..26bc54a332 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -176,46 +176,46 @@ public void testGetBundleVersion_noManifestFile() throws IOException { @Test void testGetProtobufVersion_success() throws Exception { - IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); - when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) + ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) .thenAnswer(invocationOnMock -> Class.class); - when(mockClassLoader.getFieldValue(Class.class, "MAJOR")).thenReturn("2"); - when(mockClassLoader.getFieldValue(Class.class, "MINOR")).thenReturn("3"); - when(mockClassLoader.getFieldValue(Class.class, "PATCH")).thenReturn("4"); + when(mockClassWrapper.getFieldValue(Class.class, "MAJOR")).thenReturn("2"); + when(mockClassWrapper.getFieldValue(Class.class, "MINOR")).thenReturn("3"); + when(mockClassWrapper.getFieldValue(Class.class, "PATCH")).thenReturn("4"); - String version = GaxProperties.getProtobufVersion(mockClassLoader, Any.class); + String version = GaxProperties.getProtobufVersion(mockClassWrapper, Any.class); assertEquals("2.3.4", version); } @Test void testGetProtobufVersion_classNotFoundException() throws Exception { - IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); - when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) + ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) .thenThrow(new ClassNotFoundException("")); - String version = GaxProperties.getProtobufVersion(mockClassLoader, Any.class); + String version = GaxProperties.getProtobufVersion(mockClassWrapper, Any.class); assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); } @Test void testgetProtobufVersion_noSuchFieldException() throws Exception { - IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); - when(mockClassLoader.getFieldValue(any(), any())).thenThrow(NoSuchFieldException.class); + ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + when(mockClassWrapper.getFieldValue(any(), any())).thenThrow(NoSuchFieldException.class); - String version = GaxProperties.getProtobufVersion(mockClassLoader, Any.class); + String version = GaxProperties.getProtobufVersion(mockClassWrapper, Any.class); assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); } @Test void testGetProtobufVersion_noManifest() throws Exception { - IClassLoaderWrapper mockClassLoader = mock(IClassLoaderWrapper.class); - when(mockClassLoader.loadClass("com.google.protobuf.RuntimeVersion")) + ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) .thenThrow(new ClassNotFoundException("")); - String version = GaxProperties.getProtobufVersion(mockClassLoader, GaxProperties.class); + String version = GaxProperties.getProtobufVersion(mockClassWrapper, GaxProperties.class); assertEquals("3", version); } From e87dd97549cc771e365f6f50271ae8c6c9cf6559 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:48:31 -0500 Subject: [PATCH 06/12] formatting --- .../com/google/api/gax/core/GaxProperties.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java index 7d42eca440..329351e733 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java @@ -49,8 +49,7 @@ public class GaxProperties { private static final String DEFAULT_VERSION = ""; private static final String GAX_VERSION = getLibraryVersion(GaxProperties.class, "version.gax"); private static final String JAVA_VERSION = getRuntimeVersion(); - private static final String PROTOBUF_VERSION = - getProtobufVersion(new ClassWrapper(), Any.class); + private static final String PROTOBUF_VERSION = getProtobufVersion(new ClassWrapper(), Any.class); private GaxProperties() {} @@ -151,9 +150,9 @@ static Optional getBundleVersion(Class clazz) { } /** - * Returns the Protobuf runtime version as reported by com.google.protobuf.RuntimeVersion, - * if class is available, otherwise by reading from MANIFEST file. If niether option is available defaults to - * protobuf version 3 as RuntimeVersion class is available in protobuf version 4+ + * Returns the Protobuf runtime version as reported by com.google.protobuf.RuntimeVersion, if + * class is available, otherwise by reading from MANIFEST file. If niether option is available + * defaults to protobuf version 3 as RuntimeVersion class is available in protobuf version 4+ */ @VisibleForTesting static String getProtobufVersion(ClassWrapper classWrapper, Class clazz) { @@ -165,8 +164,12 @@ static String getProtobufVersion(ClassWrapper classWrapper, Class clazz) { + classWrapper.getFieldValue(protobufRuntimeVersionClass, "MINOR") + "." + classWrapper.getFieldValue(protobufRuntimeVersionClass, "PATCH"); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | SecurityException e) { - // If manifest file is not available default to protobuf generic version 3 as we know RuntimeVersion class is + } catch (ClassNotFoundException + | NoSuchFieldException + | IllegalAccessException + | SecurityException e) { + // If manifest file is not available default to protobuf generic version 3 as we know + // RuntimeVersion class is // available in protobuf jar 4+. return getBundleVersion(clazz).orElse("3"); } From 94cfaccefb25e780b40e0a8ef7c3d578818f8c29 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:03:50 -0500 Subject: [PATCH 07/12] clean up java docs --- .../java/com/google/api/gax/util/ClassWrapper.java | 4 ++-- .../com/google/api/gax/core/GaxPropertiesTest.java | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java index 945934bf03..11047c534d 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java @@ -29,7 +29,7 @@ */ package com.google.api.gax.util; -/* Wrapper class for reflection Class methods to enable unit testing. */ +/* Wrapper class for reflection {@link java.lang.Class} methods to enable unit testing. */ public class ClassWrapper { /* Wraps {@link java.lang.Class#forName} method */ @@ -37,7 +37,7 @@ public Class forName(String name) throws ClassNotFoundException { return Class.forName(name); } - /* Consolidates retrieving a field on a Class object via reflection and retrieving the value of that field */ + /* Consolidates retrieving a {@link java.lang.Field} on a {@link java.lang.Class} object via reflection and retrieving the value of that Field */ public Object getFieldValue(Class clazz, String fieldName) throws NoSuchFieldException, IllegalAccessException { return clazz.getField(fieldName).get(null); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index 26bc54a332..b9e45c90e9 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -30,14 +30,16 @@ package com.google.api.gax.core; import static com.google.api.gax.core.GaxProperties.getBundleVersion; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.google.api.gax.util.*; +import com.google.api.gax.util.ClassWrapper; import com.google.common.base.Strings; -import com.google.protobuf.*; +import com.google.protobuf.Any; import java.io.IOException; import java.util.Optional; import java.util.regex.Pattern; From 20d98092e2854b36c8ebbe86b4090fd94ff1df10 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:40:23 -0500 Subject: [PATCH 08/12] move ClassWrapper.java to GaxProperties --- .../google/api/gax/core/GaxProperties.java | 16 ++++++- .../com/google/api/gax/util/ClassWrapper.java | 45 ------------------- .../api/gax/core/GaxPropertiesTest.java | 9 ++-- 3 files changed, 19 insertions(+), 51 deletions(-) delete mode 100644 gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java diff --git a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java index 329351e733..e4bd635312 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java @@ -30,7 +30,6 @@ package com.google.api.gax.core; import com.google.api.core.InternalApi; -import com.google.api.gax.util.ClassWrapper; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.protobuf.Any; @@ -174,4 +173,19 @@ static String getProtobufVersion(ClassWrapper classWrapper, Class clazz) { return getBundleVersion(clazz).orElse("3"); } } + + /* Wrapper class for reflection {@link java.lang.Class} methods to enable unit testing. */ + static class ClassWrapper { + + /* Wraps {@link java.lang.Class#forName} method */ + public Class forName(String name) throws ClassNotFoundException { + return Class.forName(name); + } + + /* Consolidates retrieving a {@link java.lang.Field} on a {@link java.lang.Class} object via reflection and retrieving the value of that Field */ + public Object getFieldValue(Class clazz, String fieldName) + throws NoSuchFieldException, IllegalAccessException { + return clazz.getField(fieldName).get(null); + } + } } diff --git a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java b/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java deleted file mode 100644 index 11047c534d..0000000000 --- a/gax-java/gax/src/main/java/com/google/api/gax/util/ClassWrapper.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 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.util; - -/* Wrapper class for reflection {@link java.lang.Class} methods to enable unit testing. */ -public class ClassWrapper { - - /* Wraps {@link java.lang.Class#forName} method */ - public Class forName(String name) throws ClassNotFoundException { - return Class.forName(name); - } - - /* Consolidates retrieving a {@link java.lang.Field} on a {@link java.lang.Class} object via reflection and retrieving the value of that Field */ - public Object getFieldValue(Class clazz, String fieldName) - throws NoSuchFieldException, IllegalAccessException { - return clazz.getField(fieldName).get(null); - } -} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index b9e45c90e9..c3b0b21f69 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.google.api.gax.util.ClassWrapper; import com.google.common.base.Strings; import com.google.protobuf.Any; import java.io.IOException; @@ -178,7 +177,7 @@ public void testGetBundleVersion_noManifestFile() throws IOException { @Test void testGetProtobufVersion_success() throws Exception { - ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) .thenAnswer(invocationOnMock -> Class.class); when(mockClassWrapper.getFieldValue(Class.class, "MAJOR")).thenReturn("2"); @@ -192,7 +191,7 @@ void testGetProtobufVersion_success() throws Exception { @Test void testGetProtobufVersion_classNotFoundException() throws Exception { - ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) .thenThrow(new ClassNotFoundException("")); @@ -203,7 +202,7 @@ void testGetProtobufVersion_classNotFoundException() throws Exception { @Test void testgetProtobufVersion_noSuchFieldException() throws Exception { - ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); when(mockClassWrapper.getFieldValue(any(), any())).thenThrow(NoSuchFieldException.class); String version = GaxProperties.getProtobufVersion(mockClassWrapper, Any.class); @@ -213,7 +212,7 @@ void testgetProtobufVersion_noSuchFieldException() throws Exception { @Test void testGetProtobufVersion_noManifest() throws Exception { - ClassWrapper mockClassWrapper = mock(ClassWrapper.class); + GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) .thenThrow(new ClassNotFoundException("")); From b52dd303efb689512a7f8ac906b5b2efa55c5372 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:27:04 -0500 Subject: [PATCH 09/12] refactor to remove the need for a mock class wrapper --- .../google/api/gax/core/GaxProperties.java | 32 ++++---------- .../api/gax/core/GaxPropertiesTest.java | 42 +++++++------------ 2 files changed, 22 insertions(+), 52 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java index e4bd635312..f5e805972b 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java @@ -48,7 +48,8 @@ public class GaxProperties { private static final String DEFAULT_VERSION = ""; private static final String GAX_VERSION = getLibraryVersion(GaxProperties.class, "version.gax"); private static final String JAVA_VERSION = getRuntimeVersion(); - private static final String PROTOBUF_VERSION = getProtobufVersion(new ClassWrapper(), Any.class); + private static final String PROTOBUF_VERSION = + getProtobufVersion(Any.class, "com.google.protobuf.RuntimeVersion");; private GaxProperties() {} @@ -154,38 +155,21 @@ static Optional getBundleVersion(Class clazz) { * defaults to protobuf version 3 as RuntimeVersion class is available in protobuf version 4+ */ @VisibleForTesting - static String getProtobufVersion(ClassWrapper classWrapper, Class clazz) { + static String getProtobufVersion(Class clazz, String protobufRuntimeVersionClassName) { try { - Class protobufRuntimeVersionClass = - classWrapper.forName("com.google.protobuf.RuntimeVersion"); - return classWrapper.getFieldValue(protobufRuntimeVersionClass, "MAJOR") + Class protobufRuntimeVersionClass = Class.forName(protobufRuntimeVersionClassName); + return protobufRuntimeVersionClass.getField("MAJOR").get(null) + "." - + classWrapper.getFieldValue(protobufRuntimeVersionClass, "MINOR") + + protobufRuntimeVersionClass.getField("MINOR").get(null) + "." - + classWrapper.getFieldValue(protobufRuntimeVersionClass, "PATCH"); + + protobufRuntimeVersionClass.getField("PATCH").get(null); } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | SecurityException e) { // If manifest file is not available default to protobuf generic version 3 as we know - // RuntimeVersion class is - // available in protobuf jar 4+. + // RuntimeVersion class is available in protobuf jar 4+. return getBundleVersion(clazz).orElse("3"); } } - - /* Wrapper class for reflection {@link java.lang.Class} methods to enable unit testing. */ - static class ClassWrapper { - - /* Wraps {@link java.lang.Class#forName} method */ - public Class forName(String name) throws ClassNotFoundException { - return Class.forName(name); - } - - /* Consolidates retrieving a {@link java.lang.Field} on a {@link java.lang.Class} object via reflection and retrieving the value of that Field */ - public Object getFieldValue(Class clazz, String fieldName) - throws NoSuchFieldException, IllegalAccessException { - return clazz.getField(fieldName).get(null); - } - } } diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index c3b0b21f69..ce6d71f5ce 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -33,17 +33,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; +//import com.google.api.gax.core.GaxPropertiesTest.NullMajorVersionClass import com.google.common.base.Strings; -import com.google.protobuf.Any; +import com.google.protobuf.*; + import java.io.IOException; import java.util.Optional; import java.util.regex.Pattern; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.runner.*; class GaxPropertiesTest { @@ -176,47 +177,32 @@ public void testGetBundleVersion_noManifestFile() throws IOException { } @Test - void testGetProtobufVersion_success() throws Exception { - GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); - when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) - .thenAnswer(invocationOnMock -> Class.class); - when(mockClassWrapper.getFieldValue(Class.class, "MAJOR")).thenReturn("2"); - when(mockClassWrapper.getFieldValue(Class.class, "MINOR")).thenReturn("3"); - when(mockClassWrapper.getFieldValue(Class.class, "PATCH")).thenReturn("4"); - - String version = GaxProperties.getProtobufVersion(mockClassWrapper, Any.class); + void testGetProtobufVersion_success() { + String version = GaxProperties.getProtobufVersion(Any.class, "com.google.protobuf.Any"); - assertEquals("2.3.4", version); + assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); } @Test void testGetProtobufVersion_classNotFoundException() throws Exception { - GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); - when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) - .thenThrow(new ClassNotFoundException("")); - - String version = GaxProperties.getProtobufVersion(mockClassWrapper, Any.class); + String version = GaxProperties.getProtobufVersion(Any.class, "foo.NonExistantClass"); assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); } @Test void testgetProtobufVersion_noSuchFieldException() throws Exception { - GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); - when(mockClassWrapper.getFieldValue(any(), any())).thenThrow(NoSuchFieldException.class); - - String version = GaxProperties.getProtobufVersion(mockClassWrapper, Any.class); + String version = + GaxProperties.getProtobufVersion( + Any.class, + "java.lang.Class"); assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); } @Test void testGetProtobufVersion_noManifest() throws Exception { - GaxProperties.ClassWrapper mockClassWrapper = mock(GaxProperties.ClassWrapper.class); - when(mockClassWrapper.forName("com.google.protobuf.RuntimeVersion")) - .thenThrow(new ClassNotFoundException("")); - - String version = GaxProperties.getProtobufVersion(mockClassWrapper, GaxProperties.class); + String version = GaxProperties.getProtobufVersion(GaxProperties.class, "foo.NonExistantClass"); assertEquals("3", version); } From 239288ab3b4ed6a9b4873c061b4a09a62ff37dc4 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:38:08 -0500 Subject: [PATCH 10/12] removed unused imports --- .../com/google/api/gax/core/GaxPropertiesTest.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index ce6d71f5ce..3f73e8bd54 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -33,18 +33,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.*; -//import com.google.api.gax.core.GaxPropertiesTest.NullMajorVersionClass import com.google.common.base.Strings; -import com.google.protobuf.*; - +import com.google.protobuf.Any; import java.io.IOException; import java.util.Optional; import java.util.regex.Pattern; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.junit.runner.*; class GaxPropertiesTest { @@ -192,10 +188,7 @@ void testGetProtobufVersion_classNotFoundException() throws Exception { @Test void testgetProtobufVersion_noSuchFieldException() throws Exception { - String version = - GaxProperties.getProtobufVersion( - Any.class, - "java.lang.Class"); + String version = GaxProperties.getProtobufVersion(Any.class, "java.lang.Class"); assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); } From 472d9f0f9bd9fa94fa792dd6aa17841e1bd96cd5 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:04:05 -0500 Subject: [PATCH 11/12] testing to see if this fixes sonar analysis --- .../api/gax/core/GaxPropertiesTest.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index 3f73e8bd54..6326eaae9f 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -46,7 +46,7 @@ class GaxPropertiesTest { @Test void testGaxVersion() { - Version version = readVersion(GaxProperties.getGaxVersion()); + Version version = readVersion(GaxProperties.getGaxVersion(), false); assertTrue(version.major >= 1); if (version.major == 1) { @@ -161,8 +161,9 @@ void testGetJavaRuntimeInfo_nullJavaVersion() { @Test public void testGetProtobufVersion() throws IOException { - assertTrue( - Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(GaxProperties.getProtobufVersion()).find()); + Version version = readVersion(GaxProperties.getProtobufVersion(), true); + + validateVersion(version); } @Test @@ -176,7 +177,7 @@ public void testGetBundleVersion_noManifestFile() throws IOException { void testGetProtobufVersion_success() { String version = GaxProperties.getProtobufVersion(Any.class, "com.google.protobuf.Any"); - assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); + validateVersion(readVersion(version, true)); } @Test @@ -200,23 +201,37 @@ void testGetProtobufVersion_noManifest() throws Exception { assertEquals("3", version); } - private Version readVersion(String version) { + private void validateVersion(Version version) { + assertTrue(version.major >= 3); + if (version.major == 3) { + assertTrue(version.minor >= 25); + } + assertTrue(version.patch >= 0); + } + + private Version readVersion(String version, boolean includePatch) { assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); String[] versionComponents = version.split("\\."); // This test was added in version 1.56.0, so check that the major and minor numbers are greater // than that. int major = Integer.parseInt(versionComponents[0]); int minor = Integer.parseInt(versionComponents[1]); - return new Version(major, minor); + int patch = 0; + if (includePatch) { + patch = Integer.parseInt(versionComponents[2]); + } + return new Version(major, minor, patch); } private static class Version { public int major; public int minor; + public int patch; - public Version(int major, int minor) { + public Version(int major, int minor, int patch) { this.major = major; this.minor = minor; + this.patch = patch; } } } From 0eb5fd112fbfd8fc6202953006b607592bbb6fe6 Mon Sep 17 00:00:00 2001 From: ldetmer <1771267+ldetmer@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:48:51 -0500 Subject: [PATCH 12/12] more robust unit tests --- .../google/api/gax/core/GaxProperties.java | 3 +- .../api/gax/core/GaxPropertiesTest.java | 40 ++++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java index f5e805972b..994ba2eb82 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java @@ -166,7 +166,8 @@ static String getProtobufVersion(Class clazz, String protobufRuntimeVersionClass } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException - | SecurityException e) { + | SecurityException + | NullPointerException e) { // If manifest file is not available default to protobuf generic version 3 as we know // RuntimeVersion class is available in protobuf jar 4+. return getBundleVersion(clazz).orElse("3"); diff --git a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java index 6326eaae9f..6560df4bc1 100644 --- a/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java +++ b/gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java @@ -46,7 +46,7 @@ class GaxPropertiesTest { @Test void testGaxVersion() { - Version version = readVersion(GaxProperties.getGaxVersion(), false); + Version version = readVersion(GaxProperties.getGaxVersion()); assertTrue(version.major >= 1); if (version.major == 1) { @@ -161,9 +161,8 @@ void testGetJavaRuntimeInfo_nullJavaVersion() { @Test public void testGetProtobufVersion() throws IOException { - Version version = readVersion(GaxProperties.getProtobufVersion(), true); - - validateVersion(version); + assertTrue( + Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(GaxProperties.getProtobufVersion()).find()); } @Test @@ -175,9 +174,11 @@ public void testGetBundleVersion_noManifestFile() throws IOException { @Test void testGetProtobufVersion_success() { - String version = GaxProperties.getProtobufVersion(Any.class, "com.google.protobuf.Any"); + String version = + GaxProperties.getProtobufVersion( + Any.class, "com.google.api.gax.core.GaxPropertiesTest$RuntimeVersion"); - validateVersion(readVersion(version, true)); + assertEquals("3.13.6", version); } @Test @@ -201,37 +202,30 @@ void testGetProtobufVersion_noManifest() throws Exception { assertEquals("3", version); } - private void validateVersion(Version version) { - assertTrue(version.major >= 3); - if (version.major == 3) { - assertTrue(version.minor >= 25); - } - assertTrue(version.patch >= 0); - } - - private Version readVersion(String version, boolean includePatch) { + private Version readVersion(String version) { assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find()); String[] versionComponents = version.split("\\."); // This test was added in version 1.56.0, so check that the major and minor numbers are greater // than that. int major = Integer.parseInt(versionComponents[0]); int minor = Integer.parseInt(versionComponents[1]); - int patch = 0; - if (includePatch) { - patch = Integer.parseInt(versionComponents[2]); - } - return new Version(major, minor, patch); + return new Version(major, minor); } private static class Version { public int major; public int minor; - public int patch; - public Version(int major, int minor, int patch) { + public Version(int major, int minor) { this.major = major; this.minor = minor; - this.patch = patch; } } + + // Test class that emulates com.google.protobuf.RuntimeVersion for reflection lookup of fields + class RuntimeVersion { + public static final int MAJOR = 3; + public static final int MINOR = 13; + public static final int PATCH = 6; + } }