How to create GRPC client directly from protobuf without compiling it into java code

Protobuf systems really need protoc to be run. However, the generated code could be skipped. Instead of passing something like --java_out and --grpc_java_out to protoc you can pass --descriptor_set_out=FILE which will parse the .proto file into a descriptor file. A descriptor file is a proto-encoded FileDescriptorSet. This is the same basic format as used with the reflection service.

Once you have a descriptor, you can load it a FileDescriptor at a time and create a DynamicMessage.

Then for the gRPC piece, you need to create a gRPC MethodDescriptor.

static MethodDescriptor from(
  Descriptors.MethodDescriptor methodDesc
) {
  return MethodDescriptor.<DynamicMessage, DynamicMessage>newBuilder()
    // UNKNOWN is fine, but the "correct" value can be computed from
    // methodDesc.toProto().getClientStreaming()/getServerStreaming()
    .setType(getMethodTypeFromDesc(methodDesc))
    .setFullMethodName(MethodDescriptor.generateFullMethodName(
        serviceDesc.getFullName(), methodDesc.getName()))
    .setRequestMarshaller(ProtoUtils.marshaller(
        DynamicMessage.getDefaultInstance(methodDesc.getInputType())))
    .setResponseMarshaller(ProtoUtils.marshaller(
        DynamicMessage.getDefaultInstance(methodDesc.getOutputType())))
    .build();

static MethodDescriptor.MethodType getMethodTypeFromDesc(
  Descriptors.MethodDescriptor methodDesc
) {
  if (!methodDesc.isServerStreaming()
    && !methodDesc.isClientStreaming()) {
    return MethodDescriptor.MethodType.UNARY;
  } else if (methodDesc.isServerStreaming()
        && !methodDesc.isClientStreaming()) {
    return MethodDescriptor.MethodType.SERVER_STREAMING;
  } else if (!methodDesc.isServerStreaming()) {
    return MethodDescriptor.MethodType.CLIENT_STREAMING);
  } else {
    return MethodDescriptor.MethodType.BIDI_STREAMING);
  }
}

At that point you have everything you need and can call Channel.newCall(method, CallOptions.DEFAULT) in gRPC. You’re also free to use ClientCalls to use something more similar to the stub APIs.

So dynamic calls are definitely possible, and is used for things like grpcurl. But it also is not easy and so is generally only done when necessary.

Leave a Comment