This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory-site.git

commit 9588aed292374cc0c7deecc8229f06a1c4abe27f
Author: chaokunyang <[email protected]>
AuthorDate: Sat Jun 13 07:02:23 2026 +0000

    🔄 synced local 'docs/guide/' with remote 'docs/guide/'
---
 docs/guide/java/grpc-support.md  |   5 +-
 docs/guide/scala/grpc-support.md | 397 +++++++++++++++++++++++++++++++++++++++
 docs/guide/scala/index.md        |   1 +
 docs/guide/scala/schema-idl.md   |   4 +
 4 files changed, 405 insertions(+), 2 deletions(-)

diff --git a/docs/guide/java/grpc-support.md b/docs/guide/java/grpc-support.md
index 4a233852de..7bebb010f9 100644
--- a/docs/guide/java/grpc-support.md
+++ b/docs/guide/java/grpc-support.md
@@ -30,8 +30,9 @@ 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 Kotlin coroutine stubs and service bases, see
-[Kotlin gRPC Support](../kotlin/grpc-support.md).
+For Scala generated grpc-java companions, see
+[Scala gRPC Support](../scala/grpc-support.md). For Kotlin coroutine stubs and
+service bases, see [Kotlin gRPC Support](../kotlin/grpc-support.md).
 
 ## Add Dependencies
 
diff --git a/docs/guide/scala/grpc-support.md b/docs/guide/scala/grpc-support.md
new file mode 100644
index 0000000000..0b97929db0
--- /dev/null
+++ b/docs/guide/scala/grpc-support.md
@@ -0,0 +1,397 @@
+---
+title: gRPC Support
+sidebar_position: 6
+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 Scala 3 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.
+
+## Add Dependencies
+
+Generated Scala service files compile against grpc-java. The `fory-scala`
+artifact does 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.
+
+```sbt
+libraryDependencies ++= Seq(
+  "org.apache.fory" %% "fory-scala" % "<fory-version>",
+  "io.grpc" % "grpc-api" % "<grpc-version>",
+  "io.grpc" % "grpc-stub" % "<grpc-version>",
+  "io.grpc" % "grpc-netty-shaded" % "<grpc-version>"
+)
+```
+
+Generated Scala models and gRPC service companions are Scala 3 source. The
+`fory-scala` artifact remains cross-built for Scala 2.13 and Scala 3, and the
+dependency-free `org.apache.fory.scala.rpc` handle traits are available from
+the shared artifact.
+
+## Define a Service
+
+Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers
+`rpc_service` definitions. A Fory IDL service looks like this:
+
+```protobuf
+package demo.greeter;
+
+message HelloRequest {
+  string name = 1;
+}
+
+message HelloReply {
+  string reply = 1;
+}
+
+service Greeter {
+  rpc SayHello (HelloRequest) returns (HelloReply);
+}
+```
+
+Generate Scala model and gRPC companion code with `--grpc`:
+
+```bash
+foryc service.fdl --scala_out=./generated/scala --grpc
+```
+
+For this schema, the Scala generator emits:
+
+| File                      | Purpose                                      |
+| ------------------------- | -------------------------------------------- |
+| `HelloRequest.scala`      | Fory model type for the request              |
+| `HelloReply.scala`        | Fory model type for the response             |
+| `GreeterForyModule.scala` | Fory registration module for generated types |
+| `GreeterGrpc.scala`       | grpc-java service base, client, and codecs   |
+
+## Implement a Server
+
+Extend the generated `GreeterGrpc.GreeterImplBase` class and register it with a
+standard grpc-java `Server`. Unary RPCs can be implemented with a direct
+request-to-response method:
+
+```scala
+package demo.greeter
+
+import io.grpc.ServerBuilder
+
+final class GreeterService extends GreeterGrpc.GreeterImplBase {
+  override def sayHello(request: HelloRequest): HelloReply =
+    HelloReply(s"Hello, ${request.name}")
+}
+
+@main def runServer(): Unit = {
+  val 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 client with an ordinary grpc-java channel:
+
+```scala
+package demo.greeter
+
+import io.grpc.ManagedChannelBuilder
+import scala.concurrent.Await
+import scala.concurrent.duration.DurationInt
+
+@main def runClient(): Unit = {
+  val channel = ManagedChannelBuilder
+    .forAddress("localhost", 50051)
+    .usePlaintext()
+    .build()
+  try {
+    val client = GreeterGrpc.newClient(channel)
+    val call = client.sayHello(HelloRequest("Fory"))
+    val reply = Await.result(call.asFuture, 30.seconds)
+    println(reply.reply)
+  } finally {
+    channel.shutdownNow()
+  }
+}
+```
+
+Unary Scala-friendly methods return `RpcFuture[A]`. Use `asFuture` for Scala
+composition, and call `cancel()` when the RPC should be cancelled before it
+completes. The same generated client also exposes grpc-java-style per-method
+variants such as observer-based async calls, blocking calls, and
+`ListenableFuture` unary calls.
+
+## Streaming RPCs
+
+Fory service definitions can use the same gRPC streaming shapes as grpc-java:
+
+```protobuf
+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 Scala methods use these shapes:
+
+| IDL shape               | Scala client convenience | grpc-java-style methods 
                         |
+| ----------------------- | ------------------------ | 
------------------------------------------------ |
+| Unary                   | `RpcFuture[Resp]`        | Async observer, 
blocking, and `ListenableFuture` |
+| Server streaming        | `RpcIterator[Resp]`      | Async observer and 
blocking iterator             |
+| Client streaming        | None                     | `StreamObserver` 
request stream                  |
+| Bidirectional streaming | None                     | `StreamObserver` 
request and response streams    |
+
+The Scala-friendly convenience layer covers the cases where a direct Scala
+handle can keep the important lifecycle controls. Unary calls use
+`RpcFuture[A]` so callers can compose with Scala `Future` without losing
+cancellation. Server-streaming calls use `RpcIterator[A]` so callers can 
consume
+responses with the normal Scala `Iterator` contract while still being able to
+close the underlying RPC. Client-streaming and bidirectional streaming stay on
+grpc-java `StreamObserver` APIs because the request stream lifecycle,
+completion, cancellation, and flow-control rules are the grpc-java rules.
+
+### Server-Streaming Clients
+
+Use the Scala-friendly method when the client wants pull-style consumption:
+
+```scala
+val stream = client.lotsOfReplies(HelloRequest("Fory"))
+try {
+  while (stream.hasNext) {
+    val reply = stream.next()
+    println(reply.reply)
+  }
+} finally {
+  stream.close()
+}
+```
+
+`RpcIterator[A]` extends Scala `Iterator[A]` and `AutoCloseable`. A fully
+consumed stream closes when the server completes it. If the caller stops early,
+call `close()` or `cancel()` to release the gRPC call and notify the server 
that
+the response stream is no longer needed.
+
+Use the observer overload when the client wants grpc-java async callbacks:
+
+```scala
+import io.grpc.stub.StreamObserver
+
+client.lotsOfReplies(
+  HelloRequest("Fory"),
+  new StreamObserver[HelloReply] {
+    override def onNext(value: HelloReply): Unit =
+      println(value.reply)
+
+    override def onError(t: Throwable): Unit =
+      t.printStackTrace()
+
+    override def onCompleted(): Unit =
+      println("done")
+  }
+)
+```
+
+The generated client also exposes a blocking grpc-java-style iterator through
+`lotsOfRepliesBlocking(request)`. Prefer the Scala-friendly `RpcIterator` when
+you need early cancellation; use the blocking iterator only when matching an
+existing grpc-java workflow.
+
+### Client-Streaming Clients
+
+For client-streaming RPCs, the generated method accepts a response observer and
+returns the request observer. Send every request with `onNext`, then call
+`onCompleted` exactly once when the client has finished sending:
+
+```scala
+import io.grpc.stub.StreamObserver
+
+val requests = client.lotsOfGreetings(
+  new StreamObserver[HelloReply] {
+    override def onNext(value: HelloReply): Unit =
+      println(value.reply)
+
+    override def onError(t: Throwable): Unit =
+      t.printStackTrace()
+
+    override def onCompleted(): Unit =
+      println("server completed")
+  }
+)
+
+requests.onNext(HelloRequest("Ada"))
+requests.onNext(HelloRequest("Grace"))
+requests.onCompleted()
+```
+
+If the client cannot finish sending requests, signal the failure with
+`requests.onError(error)`. Deadlines, cancellation, and call options are the
+standard grpc-java stub features, so they are configured on the generated 
client
+stub before starting the call.
+
+### Bidirectional Clients
+
+Bidirectional streaming uses the same grpc-java request observer pattern, but
+responses can arrive while the client is still sending requests:
+
+```scala
+import io.grpc.stub.StreamObserver
+
+val requests = client.chat(
+  new StreamObserver[HelloReply] {
+    override def onNext(value: HelloReply): Unit =
+      println(value.reply)
+
+    override def onError(t: Throwable): Unit =
+      t.printStackTrace()
+
+    override def onCompleted(): Unit =
+      println("chat closed")
+  }
+)
+
+requests.onNext(HelloRequest("first"))
+requests.onNext(HelloRequest("second"))
+requests.onCompleted()
+```
+
+Use grpc-java observer subtypes such as `ClientResponseObserver`,
+`ClientCallStreamObserver`, or `ServerCallStreamObserver` when an application
+needs manual inbound flow control, readiness callbacks, cancellation handlers,
+or direct transport-level cancellation. The generated Scala methods accept the
+standard grpc-java observer types, so those advanced grpc-java patterns remain
+available without a separate Fory API.
+
+### Streaming Servers
+
+Unary server methods can use the direct Scala-friendly override shown earlier.
+Streaming server methods use grpc-java observers. A server-streaming
+implementation receives one request and writes zero or more responses:
+
+```scala
+import io.grpc.stub.StreamObserver
+import scala.util.control.NonFatal
+
+final class GreeterService extends GreeterGrpc.GreeterImplBase {
+  override def lotsOfReplies(
+      request: HelloRequest,
+      responseObserver: StreamObserver[HelloReply]
+  ): Unit =
+    try {
+      responseObserver.onNext(HelloReply(s"Hello, ${request.name}"))
+      responseObserver.onNext(HelloReply(s"Welcome, ${request.name}"))
+      responseObserver.onCompleted()
+    } catch {
+      case NonFatal(e) =>
+        responseObserver.onError(e)
+    }
+}
+```
+
+Client-streaming servers return an observer for incoming requests and write the
+single response when the request stream completes:
+
+```scala
+import io.grpc.stub.StreamObserver
+import scala.collection.mutable.ArrayBuffer
+
+final class GreeterService extends GreeterGrpc.GreeterImplBase {
+  override def lotsOfGreetings(
+      responseObserver: StreamObserver[HelloReply]
+  ): StreamObserver[HelloRequest] =
+    new StreamObserver[HelloRequest] {
+      private val names = ArrayBuffer.empty[String]
+
+      override def onNext(value: HelloRequest): Unit =
+        names += value.name
+
+      override def onError(t: Throwable): Unit =
+        names.clear()
+
+      override def onCompleted(): Unit = {
+        responseObserver.onNext(HelloReply(names.mkString("Hello ", ", ", "")))
+        responseObserver.onCompleted()
+      }
+    }
+}
+```
+
+Bidirectional servers also return an observer for incoming requests, but may
+emit responses from each `onNext` call:
+
+```scala
+import io.grpc.stub.StreamObserver
+
+final class GreeterService extends GreeterGrpc.GreeterImplBase {
+  override def chat(
+      responseObserver: StreamObserver[HelloReply]
+  ): StreamObserver[HelloRequest] =
+    new StreamObserver[HelloRequest] {
+      override def onNext(value: HelloRequest): Unit =
+        responseObserver.onNext(HelloReply(s"Echo: ${value.name}"))
+
+      override def onError(t: Throwable): Unit = ()
+
+      override def onCompleted(): Unit =
+        responseObserver.onCompleted()
+    }
+}
+```
+
+Server-streaming, client-streaming, and bidirectional server methods use
+grpc-java `StreamObserver` APIs because streaming completion, request flow
+control, cancellation, and backpressure follow grpc-java behavior.
+
+## Operations
+
+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-scala` intentionally does not depend on gRPC.
+
+### Generic Protobuf Clients Cannot Read Payloads
+
+Fory-generated gRPC services use Fory bytes inside gRPC message frames, not
+protobuf message bytes. Use a Fory-generated client for Fory-generated 
services,
+or provide a separate protobuf service endpoint for generic protobuf clients.
diff --git a/docs/guide/scala/index.md b/docs/guide/scala/index.md
index 80cc257442..80cea5a40e 100644
--- a/docs/guide/scala/index.md
+++ b/docs/guide/scala/index.md
@@ -119,3 +119,4 @@ Fory Scala is built on top of Fory Java. Most configuration 
options, features, a
 - [Schema Metadata](schema-metadata.md) - Scala annotations, references, enum 
IDs, and union metadata
 - [Default Values](default-values.md) - Scala class default values support
 - [Schema IDL And Xlang](schema-idl.md) - Scala 3 generated models and 
macro-derived xlang serializers
+- [gRPC Support](grpc-support.md) - Scala 3 generated gRPC service companions
diff --git a/docs/guide/scala/schema-idl.md b/docs/guide/scala/schema-idl.md
index e9ae8c55d8..d61d04294e 100644
--- a/docs/guide/scala/schema-idl.md
+++ b/docs/guide/scala/schema-idl.md
@@ -44,6 +44,10 @@ Generated schema modules are also Fory modules. Use 
`.withModule(...)` when
 creating a custom Fory instance, or use the generated no-argument `toBytes` and
 `fromBytes` helpers when the generated default Fory instance is sufficient.
 
+Schemas with service definitions can also generate Scala 3 gRPC service
+companions with `foryc --scala_out=... --grpc`. See
+[gRPC Support](grpc-support.md) for dependencies and client/server examples.
+
 Generated helpers register message type identities before installing message
 serializers. This two-phase order lets mutually recursive message graphs build
 descriptor metadata through the normal `TypeResolver` path without temporary


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to