25
25
import io .opentelemetry .javaagent .instrumentation .hypertrace .com .google .protobuf .util .JsonFormat ;
26
26
import java .util .ArrayList ;
27
27
import java .util .HashMap ;
28
+ import java .util .HashSet ;
28
29
import java .util .List ;
29
30
import java .util .Map ;
31
+ import java .util .Set ;
30
32
import org .slf4j .Logger ;
31
33
import org .slf4j .LoggerFactory ;
32
34
35
+ /** Utility class to convert protobuf messages to JSON. */
33
36
public class ProtobufMessageConverter {
34
37
private static final Logger log = LoggerFactory .getLogger (ProtobufMessageConverter .class );
35
38
private static final Map <String , FileDescriptor > fileDescriptorCache = new HashMap <>();
@@ -75,25 +78,73 @@ private static Descriptor getRelocatedDescriptor(Descriptors.Descriptor original
75
78
String fileKey = unrelocatedFileDescriptor .getName ();
76
79
if (fileDescriptorCache .containsKey (fileKey )) {
77
80
FileDescriptor relocatedFileDescriptor = fileDescriptorCache .get (fileKey );
78
- return relocatedFileDescriptor .findMessageTypeByName (originalDescriptor .getName ());
81
+
82
+ // Check if the message type exists in the relocated descriptor
83
+ Descriptor messageType =
84
+ relocatedFileDescriptor .findMessageTypeByName (originalDescriptor .getName ());
85
+ if (messageType == null ) {
86
+ log .debug ("Message type not found in cached descriptor: {}" , originalDescriptor .getName ());
87
+ }
88
+
89
+ return messageType ;
79
90
}
80
91
81
- // Process all dependencies first
92
+ // Process all dependencies recursively, including transitive ones
93
+ FileDescriptor fileDescriptor =
94
+ processFileDescriptorWithDependencies (unrelocatedFileDescriptor , new HashSet <>());
95
+
96
+ // Find the message type in the relocated descriptor
97
+ Descriptor result = fileDescriptor .findMessageTypeByName (originalDescriptor .getName ());
98
+ if (result == null ) {
99
+ log .debug ("Message type not found in new descriptor: {}" , originalDescriptor .getName ());
100
+ }
101
+
102
+ return result ;
103
+ }
104
+
105
+ /**
106
+ * Process a file descriptor and all its dependencies recursively.
107
+ *
108
+ * @param unrelocatedFileDescriptor The file descriptor to process
109
+ * @param processedFiles Set of file names that have already been processed to avoid circular
110
+ * dependencies
111
+ * @return The relocated file descriptor
112
+ */
113
+ private static FileDescriptor processFileDescriptorWithDependencies (
114
+ Descriptors .FileDescriptor unrelocatedFileDescriptor , Set <String > processedFiles )
115
+ throws Exception {
116
+ String fileKey = unrelocatedFileDescriptor .getName ();
117
+
118
+ // Check if we've already processed this file
119
+ if (fileDescriptorCache .containsKey (fileKey )) {
120
+ return fileDescriptorCache .get (fileKey );
121
+ }
122
+
123
+ // Mark this file as processed to avoid circular dependencies
124
+ processedFiles .add (fileKey );
125
+
82
126
List <FileDescriptor > dependencies = new ArrayList <>();
127
+
128
+ // Process all direct dependencies first
83
129
for (Descriptors .FileDescriptor dependency : unrelocatedFileDescriptor .getDependencies ()) {
84
130
String depKey = dependency .getName ();
85
- if (!fileDescriptorCache .containsKey (depKey )) {
86
- // Convert the dependency file descriptor
87
- com .google .protobuf .DescriptorProtos .FileDescriptorProto depProto = dependency .toProto ();
88
- byte [] depBytes = depProto .toByteArray ();
89
- FileDescriptorProto relocatedDepProto = FileDescriptorProto .parseFrom (depBytes );
90
131
91
- // Build with empty dependencies first (we'll fill them in later)
132
+ // Skip if we've already processed this dependency in this call chain
133
+ if (processedFiles .contains (depKey )) {
134
+ if (fileDescriptorCache .containsKey (depKey )) {
135
+ dependencies .add (fileDescriptorCache .get (depKey ));
136
+ }
137
+ continue ;
138
+ }
139
+
140
+ if (!fileDescriptorCache .containsKey (depKey )) {
141
+ // Process this dependency recursively
92
142
FileDescriptor relocatedDep =
93
- FileDescriptor .buildFrom (relocatedDepProto , new FileDescriptor [] {});
94
- fileDescriptorCache .put (depKey , relocatedDep );
143
+ processFileDescriptorWithDependencies (dependency , processedFiles );
144
+ dependencies .add (relocatedDep );
145
+ } else {
146
+ dependencies .add (fileDescriptorCache .get (depKey ));
95
147
}
96
- dependencies .add (fileDescriptorCache .get (depKey ));
97
148
}
98
149
99
150
// Now build the current file descriptor with its dependencies
@@ -106,36 +157,29 @@ private static Descriptor getRelocatedDescriptor(Descriptors.Descriptor original
106
157
FileDescriptor .buildFrom (relocatedFileProto , dependencies .toArray (new FileDescriptor [0 ]));
107
158
fileDescriptorCache .put (fileKey , relocatedFileDescriptor );
108
159
109
- return relocatedFileDescriptor . findMessageTypeByName ( originalDescriptor . getName ()) ;
160
+ return relocatedFileDescriptor ;
110
161
}
111
162
112
163
/**
113
164
* Method that takes an incoming message, converts it to a relocated one, prints it as JSON using
114
165
* the relocated JsonFormat
115
166
*
116
167
* @param message The incoming (unrelocated) protobuf message.
168
+ * @return JSON string representation of the message
169
+ * @throws Exception if conversion fails
117
170
*/
118
- public static String getMessage (Message message ) {
171
+ public static String getMessage (Message message ) throws Exception {
119
172
if (message == null ) {
120
173
log .debug ("Cannot convert null message to JSON" );
121
174
return "" ;
122
175
}
123
176
124
- try {
125
- // Convert the unrelocated message into a relocated DynamicMessage.
126
- DynamicMessage relocatedMessage = convertToRelocatedDynamicMessage (message );
177
+ // Convert the unrelocated message into a relocated DynamicMessage.
178
+ DynamicMessage relocatedMessage = convertToRelocatedDynamicMessage (message );
127
179
128
- // Use the relocated JsonFormat to print the message as JSON.
129
- JsonFormat .Printer relocatedPrinter =
130
- JsonFormat .printer ().includingDefaultValueFields ().preservingProtoFieldNames ();
131
- return relocatedPrinter .print (relocatedMessage );
132
- } catch (Exception e ) {
133
- log .error ("Failed to convert message to JSON: {}" , e .getMessage (), e );
134
- if (log .isDebugEnabled ()) {
135
- log .debug ("Message type: {}" , message .getClass ().getName ());
136
- log .debug ("Message descriptor: {}" , message .getDescriptorForType ().getFullName ());
137
- }
138
- }
139
- return "" ;
180
+ // Use the relocated JsonFormat to print the message as JSON.
181
+ JsonFormat .Printer relocatedPrinter =
182
+ JsonFormat .printer ().includingDefaultValueFields ().preservingProtoFieldNames ();
183
+ return relocatedPrinter .print (relocatedMessage );
140
184
}
141
185
}
0 commit comments