Skip to content

Commit 7f6e470

Browse files
authoredNov 7, 2024··
fix: protobuf version not always getting set in headers (#3322)
In order to maximize amount of times protobuf version is logged we have updated logic as follows: 1) First try to use protbuf RuntimeVersion which is available in protobuf 4.x 2) if not available try to read using manifest file 3) if manifest file doesn't not exist then default to "3" as we know runtime < 4 Also updated showcase test to fail in the case protobuf version is missing Tested: 1) maven build using client library directly and overriding protobuf 2) spring boot started project + google cloud storage client client lib, validated that protobuf header was sent
1 parent a1db8b7 commit 7f6e470

File tree

5 files changed

+79
-10
lines changed

5 files changed

+79
-10
lines changed
 

‎gax-java/gax/src/main/java/com/google/api/gax/core/GaxProperties.java

+26-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class GaxProperties {
4949
private static final String GAX_VERSION = getLibraryVersion(GaxProperties.class, "version.gax");
5050
private static final String JAVA_VERSION = getRuntimeVersion();
5151
private static final String PROTOBUF_VERSION =
52-
getBundleVersion(Any.class).orElse(DEFAULT_VERSION);
52+
getProtobufVersion(Any.class, "com.google.protobuf.RuntimeVersion");;
5353

5454
private GaxProperties() {}
5555

@@ -148,4 +148,29 @@ static Optional<String> getBundleVersion(Class<?> clazz) {
148148
return Optional.empty();
149149
}
150150
}
151+
152+
/**
153+
* Returns the Protobuf runtime version as reported by com.google.protobuf.RuntimeVersion, if
154+
* class is available, otherwise by reading from MANIFEST file. If niether option is available
155+
* defaults to protobuf version 3 as RuntimeVersion class is available in protobuf version 4+
156+
*/
157+
@VisibleForTesting
158+
static String getProtobufVersion(Class clazz, String protobufRuntimeVersionClassName) {
159+
try {
160+
Class<?> protobufRuntimeVersionClass = Class.forName(protobufRuntimeVersionClassName);
161+
return protobufRuntimeVersionClass.getField("MAJOR").get(null)
162+
+ "."
163+
+ protobufRuntimeVersionClass.getField("MINOR").get(null)
164+
+ "."
165+
+ protobufRuntimeVersionClass.getField("PATCH").get(null);
166+
} catch (ClassNotFoundException
167+
| NoSuchFieldException
168+
| IllegalAccessException
169+
| SecurityException
170+
| NullPointerException e) {
171+
// If manifest file is not available default to protobuf generic version 3 as we know
172+
// RuntimeVersion class is available in protobuf jar 4+.
173+
return getBundleVersion(clazz).orElse("3");
174+
}
175+
}
151176
}

‎gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiClientHeaderProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private static String checkAndAppendProtobufVersionIfNecessary(
8888
// TODO(b/366417603): appending protobuf version to existing client library token until resolved
8989
Pattern pattern = Pattern.compile("(gccl|gapic)\\S*");
9090
Matcher matcher = pattern.matcher(apiClientHeaderValue);
91-
if (matcher.find()) {
91+
if (matcher.find() && GaxProperties.getProtobufVersion() != null) {
9292
return apiClientHeaderValue.substring(0, matcher.end())
9393
+ "--"
9494
+ PROTOBUF_HEADER_VERSION_KEY
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
"name": "com.google.protobuf.RuntimeVersion",
4+
"fields" : [
5+
{ "name" : "MAJOR" },
6+
{ "name" : "MINOR" },
7+
{ "name" : "PATCH" }
8+
]
9+
}
10+
]

‎gax-java/gax/src/test/java/com/google/api/gax/core/GaxPropertiesTest.java

+40-6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import static org.junit.jupiter.api.Assertions.assertTrue;
3636

3737
import com.google.common.base.Strings;
38+
import com.google.protobuf.Any;
3839
import java.io.IOException;
3940
import java.util.Optional;
4041
import java.util.regex.Pattern;
@@ -160,12 +161,8 @@ void testGetJavaRuntimeInfo_nullJavaVersion() {
160161

161162
@Test
162163
public void testGetProtobufVersion() throws IOException {
163-
Version version = readVersion(GaxProperties.getProtobufVersion());
164-
165-
assertTrue(version.major >= 3);
166-
if (version.major == 3) {
167-
assertTrue(version.minor >= 25);
168-
}
164+
assertTrue(
165+
Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(GaxProperties.getProtobufVersion()).find());
169166
}
170167

171168
@Test
@@ -175,6 +172,36 @@ public void testGetBundleVersion_noManifestFile() throws IOException {
175172
assertFalse(version.isPresent());
176173
}
177174

175+
@Test
176+
void testGetProtobufVersion_success() {
177+
String version =
178+
GaxProperties.getProtobufVersion(
179+
Any.class, "com.google.api.gax.core.GaxPropertiesTest$RuntimeVersion");
180+
181+
assertEquals("3.13.6", version);
182+
}
183+
184+
@Test
185+
void testGetProtobufVersion_classNotFoundException() throws Exception {
186+
String version = GaxProperties.getProtobufVersion(Any.class, "foo.NonExistantClass");
187+
188+
assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find());
189+
}
190+
191+
@Test
192+
void testgetProtobufVersion_noSuchFieldException() throws Exception {
193+
String version = GaxProperties.getProtobufVersion(Any.class, "java.lang.Class");
194+
195+
assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find());
196+
}
197+
198+
@Test
199+
void testGetProtobufVersion_noManifest() throws Exception {
200+
String version = GaxProperties.getProtobufVersion(GaxProperties.class, "foo.NonExistantClass");
201+
202+
assertEquals("3", version);
203+
}
204+
178205
private Version readVersion(String version) {
179206
assertTrue(Pattern.compile("^\\d+\\.\\d+\\.\\d+").matcher(version).find());
180207
String[] versionComponents = version.split("\\.");
@@ -194,4 +221,11 @@ public Version(int major, int minor) {
194221
this.minor = minor;
195222
}
196223
}
224+
225+
// Test class that emulates com.google.protobuf.RuntimeVersion for reflection lookup of fields
226+
class RuntimeVersion {
227+
public static final int MAJOR = 3;
228+
public static final int MINOR = 13;
229+
public static final int PATCH = 6;
230+
}
197231
}

‎showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITVersionHeaders.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ void testHttpJsonCompliance_userApiVersionSetSuccess() throws IOException {
241241
@Test
242242
void testGrpcCall_sendsCorrectApiClientHeader() {
243243
Pattern defautlGrpcHeaderPattern =
244-
Pattern.compile("gl-java/.* gapic/.*?--protobuf-.* gax/.* grpc/.* protobuf/.*");
244+
Pattern.compile("gl-java/.* gapic/.*?--protobuf-\\d.* gax/.* grpc/.* protobuf/\\d.*");
245245
grpcClient.echo(EchoRequest.newBuilder().build());
246246
String headerValue = grpcInterceptor.metadata.get(API_CLIENT_HEADER_KEY);
247247
assertTrue(defautlGrpcHeaderPattern.matcher(headerValue).matches());
@@ -250,7 +250,7 @@ void testGrpcCall_sendsCorrectApiClientHeader() {
250250
@Test
251251
void testHttpJson_sendsCorrectApiClientHeader() {
252252
Pattern defautlHttpHeaderPattern =
253-
Pattern.compile("gl-java/.* gapic/.*?--protobuf-.* gax/.* rest/ protobuf/.*");
253+
Pattern.compile("gl-java/.* gapic/.*?--protobuf-\\d.* gax/.* rest/ protobuf/\\d.*");
254254
httpJsonClient.echo(EchoRequest.newBuilder().build());
255255
ArrayList<String> headerValues =
256256
(ArrayList<String>)

0 commit comments

Comments
 (0)
Please sign in to comment.