Skip to content
This repository was archived by the owner on Sep 26, 2023. It is now read-only.

Commit 01d395f

Browse files
authored
feat: relocate protobuf configurations from java-core to gax-java (#1641)
* feat: relocate ProtobufMessageFeature from java-core to gax-grpc
1 parent b8d9e30 commit 01d395f

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package com.google.api.gax.grpc.nativeimage;
32+
33+
import com.google.api.gax.nativeimage.NativeImageUtils;
34+
import com.oracle.svm.core.annotate.AutomaticFeature;
35+
import java.lang.reflect.Method;
36+
import java.util.Arrays;
37+
import java.util.List;
38+
import org.graalvm.nativeimage.hosted.Feature;
39+
import org.graalvm.nativeimage.hosted.RuntimeReflection;
40+
41+
/**
42+
* A optional feature which registers reflective usages of the GRPC Protobuf libraries.
43+
*
44+
* <p>This feature is only needed if you need to access proto objects reflectively (such as
45+
* printing/logging proto objects).
46+
*/
47+
@AutomaticFeature
48+
final class ProtobufMessageFeature implements Feature {
49+
50+
// Proto classes to check on the classpath.
51+
private static final String PROTO_MESSAGE_CLASS = "com.google.protobuf.GeneratedMessageV3";
52+
private static final String PROTO_ENUM_CLASS = "com.google.protobuf.ProtocolMessageEnum";
53+
private static final String ENUM_VAL_DESCRIPTOR_CLASS =
54+
"com.google.protobuf.Descriptors$EnumValueDescriptor";
55+
56+
// Prefixes of methods accessed reflectively by
57+
// com.google.protobuf.GeneratedMessageV3$ReflectionInvoker
58+
private static final List<String> METHOD_ACCESSOR_PREFIXES =
59+
Arrays.asList("get", "set", "has", "add", "clear", "newBuilder");
60+
61+
@Override
62+
public void beforeAnalysis(BeforeAnalysisAccess access) {
63+
Class<?> protoMessageClass = access.findClassByName(PROTO_MESSAGE_CLASS);
64+
if (protoMessageClass != null) {
65+
Method internalAccessorMethod =
66+
NativeImageUtils.getMethodOrFail(protoMessageClass, "internalGetFieldAccessorTable");
67+
68+
// Finds every class whose `internalGetFieldAccessorTable()` is reached and registers it.
69+
// `internalGetFieldAccessorTable()` is used downstream to access the class reflectively.
70+
access.registerMethodOverrideReachabilityHandler(
71+
(duringAccess, method) -> {
72+
registerFieldAccessors(method.getDeclaringClass());
73+
registerFieldAccessors(getBuilderClass(method.getDeclaringClass()));
74+
},
75+
internalAccessorMethod);
76+
}
77+
78+
Class<?> protoEnumClass = access.findClassByName(PROTO_ENUM_CLASS);
79+
if (protoEnumClass != null) {
80+
// Finds every reachable proto enum class and registers specific methods for reflection.
81+
access.registerSubtypeReachabilityHandler(
82+
(duringAccess, subtypeClass) -> {
83+
if (!PROTO_ENUM_CLASS.equals(subtypeClass.getName())) {
84+
Method method =
85+
NativeImageUtils.getMethodOrFail(
86+
subtypeClass,
87+
"valueOf",
88+
duringAccess.findClassByName(ENUM_VAL_DESCRIPTOR_CLASS));
89+
RuntimeReflection.register(method);
90+
91+
method = NativeImageUtils.getMethodOrFail(subtypeClass, "getValueDescriptor");
92+
RuntimeReflection.register(method);
93+
}
94+
},
95+
protoEnumClass);
96+
}
97+
}
98+
99+
/** Given a proto class, registers the public accessor methods for the provided proto class. */
100+
private static void registerFieldAccessors(Class<?> protoClass) {
101+
for (Method method : protoClass.getMethods()) {
102+
boolean hasAccessorPrefix =
103+
METHOD_ACCESSOR_PREFIXES.stream().anyMatch(prefix -> method.getName().startsWith(prefix));
104+
if (hasAccessorPrefix) {
105+
RuntimeReflection.register(method);
106+
}
107+
}
108+
}
109+
110+
/** Given a proto class, returns the Builder nested class. */
111+
private static Class<?> getBuilderClass(Class<?> protoClass) {
112+
for (Class<?> clazz : protoClass.getClasses()) {
113+
if (clazz.getName().endsWith("Builder")) {
114+
return clazz;
115+
}
116+
}
117+
return null;
118+
}
119+
}

0 commit comments

Comments
 (0)