resteasy-grpc: Handling arrays
By Ron Sigal | January 23, 2024
Release 1.0.0.Alpha5 of resteasy-grpc has a new feature for handling arbitrary arrays. Although protobuf comes with a representation of one dimension arrays, e.g.
message ints {
repeated int64 is = 1;
}
represents an array int[]
, there is no built-in way of handling multidimensional arrays like int[][]
, so we
have to do it explicitly.
The mechanism has two parts:
- arrays.proto, which defines
dev_resteasy_grpc_arrays___ArrayHolder
, and dev.resteasy.grpc.arrays.ArrayUtility
in grpc-bridge-runtime.
arrays.proto looks like this:
message dev_resteasy_grpc_arrays___BooleanArray {
repeated bool bool_field = 1;
}
message dev_resteasy_grpc_arrays___ByteArray {
bytes bytes_field = 1;
}
...
message dev_resteasy_grpc_arrays___AnyArray {
repeated google.protobuf.Any any_field = 1;
}
message dev_resteasy_grpc_arrays___ArrayHolderArray {
repeated dev_resteasy_grpc_arrays___ArrayHolder arrayHolder_field = 1;
}
message dev_resteasy_grpc_arrays___ArrayHolder {
string componentClass = 1;
oneof messageType {
dev_resteasy_grpc_arrays___BooleanArray booleanArray_field = 3;
dev_resteasy_grpc_arrays___ByteArray byteArray_field = 4;
...
dev_resteasy_grpc_arrays___AnyArray anyArray_field = 12;
dev_resteasy_grpc_arrays___ArrayHolderArray arrayHolderArray_field = 13;
}
}
It starts with a definition of array message types for
- all primitive types,
google.protobuf.Any
, anddev_resteasy_grpc_arrays___ArrayHolder
itself.
Then, dev_resteasy_grpc_arrays___ArrayHolder
is defined with a oneof field that can
hold any of these array message types. The self-referential field
dev_resteasy_grpc_arrays___ArrayHolderArray arrayHolderArray_field = 13;
is what allows a dev_resteasy_grpc_arrays___ArrayHolder
to represent arrays with any number of
dimension.
Compiling arrays.proto generates dev.resteasy.grpc.arrays.Array_proto
, which gives us a
gateway into the javabuf1 world. Suppose we want to generate a representation of
int[] {3, 5}
. That would look like
dev_resteasy_grpc_arrays___IntArray.Builder iab = dev_resteasy_grpc_arrays___IntArray.newBuilder();
iab.addIntField(3);
iab.addIntField(5);
dev_resteasy_grpc_arrays___IntArray ia = iab.build();
dev_resteasy_grpc_arrays___ArrayHolder.Builder ahb = dev_resteasy_grpc_arrays___ArrayHolder.newBuilder();
ahb.setIntArrayField(ia);
ahb.setComponentClass(int.class.getName());
dev_resteasy_grpc_arrays___ArrayHolder ah = ahb.build();
System.out.println(ah);
The output would be
componentClass: "int"
intArray_field {
int_field: 1
int_field: 2
}
A similar, but rather longer, sequence would be required to build a javabuf representation
of int[][] {{3, 5}, {7, 11, 13}}
.
To avoid the mess, grpc-bridge-runtime includes the class dev.resteasy.grpc.arrays.ArrayUtility
. With ArrayUtility
,
building the javabuf representation of int[][] {{3, 5}, {7, 11, 13}}
is as easy as
dev_resteasy_grpc_arrays___ArrayHolder holder = ArrayUtility.getHolder(new int[][] {{3, 5}, {7, 11, 13}});
Moreover, ArrayUtility
can turn the dev_resteasy_grpc_arrays___ArrayHolder
back to the original array:
Object array = ArrayUtility.getArray(holder);
Assert.assertArrayEquals(new int[][] {{3, 5}, {7, 11, 13}}, (int[][]) array);
These two calls to ArrayUtility
depend on the fact that the target array is built from a primitive Java type. If the
array uses an application specific type, then there are two alternative calls that can be used:
public static dev_resteasy_grpc_arrays___ArrayHolder getHolder(JavabufTranslator translator, Object o);
and
public static Object getArray(JavabufTranslator translator, Array_proto.dev_resteasy_grpc_arrays___ArrayHolder ah) throws Exception;
Also, if an application uses arrays, the generated JavabufTranslator
incorporates ArrayUtility
, so that it can be
used instead:
dev_resteasy_grpc_arrays___ArrayHolder ah = (dev_resteasy_grpc_arrays___ArrayHolder) translator.translateToJavabuf(new int[][] {{3, 5}, {7, 11, 13}});
Object array = translator.translateFromJavabuf(ah);
Assert.assertArrayEquals(new int[][] {{3, 5}, {7, 11, 13}}, (int[][]) array);
Note. The latter point can be usefully expanded, independent of the presence of arrays. Consider the class
package dev.resteasy.grpc.example;
public class C {
private int i;
private double d;
private String s;
public C(int i, double d, String s) {
this.i = i;
this.d = d;
this.s = s;
}
}
Using the fluent methods created in, say, C_proto
by the protobuf parser, an instance of
C_proto.dev_resteasy_grpc_example___C
can be created by
C_proto.dev_resteasy_grpc_example___C.Builder cb = C_proto.dev_resteasy_grpc_example___C.newBuilder();
C_proto.dev_resteasy_grpc_example___C c1 = cb.setI(3).setD(5.0).setS("seven").build();
Note that each field must be set individually. On the other hand, given the C(int, double, String)
constructor,
an instance of C_proto.dev_resteasy_grpc_example___C
can be created more directly:
C_proto.dev_resteasy_grpc_example___C c2 = (C_proto.dev_resteasy_grpc_example___C) translator.translateToJavabuf(new C(3, 5.0, "seven"));
They accomplish the same thing, so the choice is a matter of taste.
Notes
at https://github.com/resteasy/resteasy-grpc/blob/main/docs/grpc-bridge.md for a discussion of javabuf classes
-
See the documentation ↩
Useful Links
YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of YourKit Java Profiler, YourKit .NET Profiler, and YourKit YouMonitor