title: gRPC Support sidebar_position: 17 id: grpc_support license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Fory can generate Java gRPC service companions for schemas that define services. The generated service code uses normal grpc-java channels, servers, deadlines, status codes, interceptors, and transport security, while request and response objects are serialized with Fory instead of protobuf.

Use this mode when both sides of the RPC are generated from the same Fory IDL, protobuf IDL, or FlatBuffers IDL and you want gRPC transport semantics with Fory payload encoding. Use standard protobuf gRPC code generation when your API must be consumed by generic protobuf clients, reflection tools, or components that expect protobuf message bytes.

For Scala generated grpc-java companions, see Scala gRPC Support. For Kotlin coroutine stubs and service bases, see Kotlin gRPC Support.

Add Dependencies

The generated Java service files compile against grpc-java. Fory Java artifacts do not add gRPC as a hard dependency, so add grpc-java dependencies in your application build and align the version with the rest of your service stack.

Maven:

<dependencies>
  <dependency>
    <groupId>org.apache.fory</groupId>
    <artifactId>fory-core</artifactId>
    <version>${fory.version}</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-api</artifactId>
    <version>${grpc.version}</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>${grpc.version}</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>${grpc.version}</version>
  </dependency>
</dependencies>

Gradle:

dependencies {
  implementation("org.apache.fory:fory-core:$foryVersion")
  implementation("io.grpc:grpc-api:$grpcVersion")
  implementation("io.grpc:grpc-stub:$grpcVersion")
  implementation("io.grpc:grpc-netty-shaded:$grpcVersion")
}

Define a Service

Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers rpc_service definitions. A Fory IDL service looks like this:

package demo.greeter;

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string reply = 1;
}

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

Generate Java model and gRPC companion code with --grpc:

foryc service.fdl --java_out=./generated/java --grpc

For this schema, the Java generator emits:

FilePurpose
HelloRequest.javaFory model type for the request
HelloReply.javaFory model type for the response
GreeterForyModule.javaFory registration module for generated types
GreeterGrpc.javagrpc-java service base class, stubs, codecs

Implement a Server

Extend the generated GreeterGrpc.GreeterImplBase class and register it with a standard grpc-java Server.

package demo.greeter;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;

final class GreeterService extends GreeterGrpc.GreeterImplBase {
  @Override
  public void sayHello(
      HelloRequest request, StreamObserver<HelloReply> responseObserver) {
    HelloReply reply = new HelloReply();
    reply.setReply("Hello, " + request.getName());
    responseObserver.onNext(reply);
    responseObserver.onCompleted();
  }
}

public final class GreeterServer {
  public static void main(String[] args) throws Exception {
    Server server =
        ServerBuilder.forPort(50051)
            .addService(new GreeterService())
            .build()
            .start();
    server.awaitTermination();
  }
}

Generated request and response types are registered by the generated code, so service implementations do not perform manual serializer registration.

Create a Client

Use the generated stubs with an ordinary grpc-java channel:

package demo.greeter;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public final class GreeterClient {
  public static void main(String[] args) {
    ManagedChannel channel =
        ManagedChannelBuilder.forAddress("localhost", 50051)
            .usePlaintext()
            .build();
    try {
      GreeterGrpc.GreeterBlockingStub stub =
          GreeterGrpc.newBlockingStub(channel);

      HelloRequest request = new HelloRequest();
      request.setName("Fory");
      HelloReply reply = stub.sayHello(request);
      System.out.println(reply.getReply());
    } finally {
      channel.shutdownNow();
    }
  }
}

For asynchronous calls, use GreeterGrpc.newStub(channel). For future-based unary calls, use GreeterGrpc.newFutureStub(channel).

Streaming RPCs

Fory service definitions can use the same gRPC streaming shapes:

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
  rpc LotsOfReplies (HelloRequest) returns (stream HelloReply);
  rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply);
  rpc Chat (stream HelloRequest) returns (stream HelloReply);
}

Generated Java service methods follow grpc-java conventions:

IDL shapeServer method shapeClient method shape
rpc A (Req) returns (Res)void a(Req request, StreamObserver<Res> responses)blocking, async, and future unary stub
rpc A (Req) returns (stream Res)void a(Req request, StreamObserver<Res> responses)blocking iterator or async observer
rpc A (stream Req) returns (Res)StreamObserver<Req> a(StreamObserver<Res> responses)async request observer
rpc A (stream Req) returns (stream Res)StreamObserver<Req> a(StreamObserver<Res> responses)async request observer

Server implementations can use the generated streaming method shapes directly:

package demo.greeter;

import io.grpc.stub.StreamObserver;
import java.util.ArrayList;
import java.util.List;

final class GreeterService extends GreeterGrpc.GreeterImplBase {
  @Override
  public void lotsOfReplies(
      HelloRequest request, StreamObserver<HelloReply> responseObserver) {
    HelloReply first = new HelloReply();
    first.setReply("Hello, " + request.getName());
    responseObserver.onNext(first);

    HelloReply second = new HelloReply();
    second.setReply("Welcome, " + request.getName());
    responseObserver.onNext(second);
    responseObserver.onCompleted();
  }

  @Override
  public StreamObserver<HelloRequest> lotsOfGreetings(
      StreamObserver<HelloReply> responseObserver) {
    List<String> names = new ArrayList<>();
    return new StreamObserver<>() {
      @Override
      public void onNext(HelloRequest request) {
        names.add(request.getName());
      }

      @Override
      public void onError(Throwable error) {
        responseObserver.onError(error);
      }

      @Override
      public void onCompleted() {
        HelloReply reply = new HelloReply();
        reply.setReply(String.join(", ", names));
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
      }
    };
  }

  @Override
  public StreamObserver<HelloRequest> chat(
      StreamObserver<HelloReply> responseObserver) {
    return new StreamObserver<>() {
      @Override
      public void onNext(HelloRequest request) {
        HelloReply reply = new HelloReply();
        reply.setReply("Hello, " + request.getName());
        responseObserver.onNext(reply);
      }

      @Override
      public void onError(Throwable error) {
        responseObserver.onError(error);
      }

      @Override
      public void onCompleted() {
        responseObserver.onCompleted();
      }
    };
  }
}

Generated clients return the standard grpc-java call shapes:

package demo.greeter;

import io.grpc.stub.StreamObserver;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

final class StreamingClient {
  private final GreeterGrpc.GreeterBlockingStub blockingStub;
  private final GreeterGrpc.GreeterStub asyncStub;

  StreamingClient(
      GreeterGrpc.GreeterBlockingStub blockingStub,
      GreeterGrpc.GreeterStub asyncStub) {
    this.blockingStub = blockingStub;
    this.asyncStub = asyncStub;
  }

  void run() throws InterruptedException {
    Iterator<HelloReply> replies =
        blockingStub.lotsOfReplies(newRequest("Fory"));
    while (replies.hasNext()) {
      System.out.println(replies.next().getReply());
    }

    CountDownLatch greetingsDone = new CountDownLatch(1);
    StreamObserver<HelloRequest> greetings =
        asyncStub.lotsOfGreetings(new StreamObserver<>() {
          @Override
          public void onNext(HelloReply reply) {
            System.out.println(reply.getReply());
          }

          @Override
          public void onError(Throwable error) {
            greetingsDone.countDown();
          }

          @Override
          public void onCompleted() {
            greetingsDone.countDown();
          }
        });
    greetings.onNext(newRequest("Ada"));
    greetings.onNext(newRequest("Grace"));
    greetings.onCompleted();
    greetingsDone.await(5, TimeUnit.SECONDS);

    CountDownLatch chatDone = new CountDownLatch(1);
    StreamObserver<HelloRequest> chat =
        asyncStub.chat(new StreamObserver<>() {
          @Override
          public void onNext(HelloReply reply) {
            System.out.println(reply.getReply());
          }

          @Override
          public void onError(Throwable error) {
            chatDone.countDown();
          }

          @Override
          public void onCompleted() {
            chatDone.countDown();
          }
        });
    chat.onNext(newRequest("Fory"));
    chat.onCompleted();
    chatDone.await(5, TimeUnit.SECONDS);
  }

  private static HelloRequest newRequest(String name) {
    HelloRequest request = new HelloRequest();
    request.setName(name);
    return request;
  }
}

The generated descriptors preserve the exact IDL service and method names for the gRPC path.

gRPC Runtime Behavior

The generated service code only replaces request and response serialization. All normal gRPC operational features still belong to grpc-java:

  • Deadlines and cancellations
  • TLS and authentication
  • Name resolution and load balancing
  • Client and server interceptors
  • Status codes and metadata
  • Channel pooling and lifecycle management

Troubleshooting

Missing io.grpc or Guava Classes

Add the grpc-java dependencies shown above. Generated Fory service files import grpc-java APIs, but Fory Java artifacts intentionally do not depend on gRPC.

UNIMPLEMENTED

Confirm that the generated service implementation is registered with ServerBuilder.addService(...), and that the client and server were generated from the same package, service, and method names.

Protobuf Clients Cannot Decode the Service

Fory gRPC companions do not use protobuf wire encoding for messages. Use a Fory-generated client for Fory-generated services, or provide a separate protobuf service endpoint for generic protobuf clients.