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:

  1. arrays.proto, which defines dev_resteasy_grpc_arrays___ArrayHolder, and
  2. 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

  1. all primitive types,
  2. google.protobuf.Any, and
  3. dev_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

  1. See the documentation 

         

YourKit
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