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

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new 033500b6e5 feat: optionally bind ScalaPB messages to the protobuf 
serializer (#3651)
033500b6e5 is described below

commit 033500b6e538a049f3d8bc4452dbd4d867cb5c3e
Author: PJ Fanning <[email protected]>
AuthorDate: Fri Oct 23 06:39:02 2026 +0100

    feat: optionally bind ScalaPB messages to the protobuf serializer (#3651)
    
    * feat: optionally bind ScalaPB messages to the protobuf serializer
    
    Motivation:
    ProtobufSerializer can serialize ScalaPB generated messages and the
    protobuf allow list already contains scalapb.GeneratedMessageCompanion,
    but users had to add the scalapb.GeneratedMessage serialization binding
    themselves.
    
    Modification:
    - Add an optional "scalapb.GeneratedMessage" = proto serialization
      binding to the remote reference.conf.
    - Only use the binding when scalapb.GeneratedMessage can be loaded, in
      the same way as the optional com.google.protobuf binding.
    - Mention the binding in the serialization docs.
    
    Result:
    ScalaPB messages are serialized with ProtobufSerializer out of the box
    when ScalaPB is on the classpath. Nothing changes when ScalaPB is absent.
    
    Rolling upgrade consideration: applications that have ScalaPB on the
    classpath and relied on another binding for those messages (for example
    a binding on a shared marker trait or interface) may now select
    ProtobufSerializer instead, since scalapb.GeneratedMessage can be the
    more specific match. Nodes running different versions could then
    serialize those messages with different serializers during a rolling
    upgrade. Such applications can keep their previous behaviour by setting
    "scalapb.GeneratedMessage" = none, or an explicit binding, in
    pekko.actor.serialization-bindings.
    
    Tests:
    - Not run locally - left to CI; scalafmt applied
    
    References:
    None - convenience binding for ScalaPB users
    
    * docs: document opting out of the ScalaPB binding in the migration guide
    
    Motivation:
    The new default `scalapb.GeneratedMessage` binding can change how ScalaPB
    messages are serialized for users upgrading from Pekko 1.x.
    
    Modification:
    Add an entry to the serialization bindings section of the 1.x to 2.x
    migration guide explaining the change and how to disable the binding with
    `"scalapb.GeneratedMessage" = none`.
    
    Result:
    Users doing a rolling update know how to keep the Pekko 1.x behavior.
    
    Tests:
    - Not run - docs only
    
    References:
    Refs #3651
---
 .../main/scala/org/apache/pekko/serialization/Serialization.scala | 5 ++++-
 docs/src/main/paradox/migration/migration-guide-1.x-2.x.md        | 6 ++++++
 docs/src/main/paradox/serialization.md                            | 2 ++
 remote/src/main/resources/reference.conf                          | 2 ++
 .../pekko/remote/serialization/ProtobufSerializerSpec.scala       | 8 ++++++++
 5 files changed, 22 insertions(+), 1 deletion(-)

diff --git 
a/actor/src/main/scala/org/apache/pekko/serialization/Serialization.scala 
b/actor/src/main/scala/org/apache/pekko/serialization/Serialization.scala
index 6c0d8ec922..6951abd779 100644
--- a/actor/src/main/scala/org/apache/pekko/serialization/Serialization.scala
+++ b/actor/src/main/scala/org/apache/pekko/serialization/Serialization.scala
@@ -462,7 +462,7 @@ class Serialization(val system: ExtendedActorSystem) 
extends Extension {
   private[pekko] val bindings: Seq[ClassSerializer] = {
     val fromConfig = for {
       (className: String, alias: String) <- settings.SerializationBindings
-      if alias != "none" && checkGoogleProtobuf(className)
+      if alias != "none" && checkGoogleProtobuf(className) && 
checkScalaPb(className)
     } yield (system.dynamicAccess.getClassFor[Any](className).get, 
serializers(alias))
 
     val fromSettings = serializerDetails.flatMap { detail =>
@@ -500,6 +500,9 @@ class Serialization(val system: ExtendedActorSystem) 
extends Extension {
   // include "com.google.protobuf.GeneratedMessage" = proto in configured 
serialization-bindings.
   private def checkGoogleProtobuf(className: String): Boolean = 
checkClass("com.google.protobuf", className)
 
+  // for convenience scalapb.GeneratedMessage is defined in reference.conf, 
but might not be included in classpath
+  private def checkScalaPb(className: String): Boolean = 
checkClass("scalapb.GeneratedMessage", className)
+
   private def checkClass(prefix: String, className: String): Boolean =
     !className.startsWith(prefix) || 
system.dynamicAccess.getClassFor[Any](className).isSuccess
 
diff --git a/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md 
b/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
index c5b6cd97b6..c4bddca295 100644
--- a/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
+++ b/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
@@ -221,6 +221,12 @@ these messages as an ordinary `ByteString`. 
([PR2924](https://github.com/apache/
 instead of `ShardingEnvelope`. `ShardingEnvelope` is serialized as before, but 
the serializer also
 handles new Sharded Daemon Process messages (its state, and the commands for 
getting and changing the
 number of processes) that Pekko 1.x nodes cannot deserialize. 
([PR2755](https://github.com/apache/pekko/pull/2755))
+* When remoting is enabled and ScalaPB is on the classpath, ScalaPB generated 
messages
+(`scalapb.GeneratedMessage`) are now bound to the `proto` serializer by 
default. If your ScalaPB
+messages were serialized with another serializer in Pekko 1.x, mixed-version 
nodes would disagree
+during a rolling update. To keep the Pekko 1.x behavior, disable the new 
binding:
+`pekko.actor.serialization-bindings { "scalapb.GeneratedMessage" = none }`.
+([PR3651](https://github.com/apache/pekko/pull/3651))
 * The new `FilteredPayload` placeholder, which is stored in the journal in 
place of a filtered event
 (for example with events-by-slice queries and projections), has a new 
serializer with identifier `34`.
 Pekko 1.x cannot deserialize it, so do not run Pekko 1.x against a journal 
once such events have been
diff --git a/docs/src/main/paradox/serialization.md 
b/docs/src/main/paradox/serialization.md
index 74e9c525b1..4368d4ef36 100644
--- a/docs/src/main/paradox/serialization.md
+++ b/docs/src/main/paradox/serialization.md
@@ -65,6 +65,8 @@ you would need to reference it as `Wrapper$Message` instead 
of `Wrapper.Message`
 Pekko provides serializers for several primitive types and 
[protobuf](https://github.com/protocolbuffers/protobuf)
 
@javadoc[com.google.protobuf.GeneratedMessage](com.google.protobuf.GeneratedMessage)
 by default, so normally you don't need to add
 configuration for that if you send raw protobuf messages as actor messages.
+Messages generated by [ScalaPB](https://scalapb.github.io/) 
(`scalapb.GeneratedMessage`) are also bound to the
+protobuf serializer by default when ScalaPB is on the classpath.
 
 ### Programmatic
 
diff --git a/remote/src/main/resources/reference.conf 
b/remote/src/main/resources/reference.conf
index 4f7702b322..4d752322aa 100644
--- a/remote/src/main/resources/reference.conf
+++ b/remote/src/main/resources/reference.conf
@@ -42,6 +42,8 @@ pekko {
       # This com.google.protobuf serialization binding is only used if the 
class can be loaded,
       # i.e. com.google.protobuf dependency has been added in the application 
project.
       "com.google.protobuf.GeneratedMessage" = proto
+      # optional binding, only used if the ScalaPB dependency has been added 
in the application project
+      "scalapb.GeneratedMessage" = proto
 
       "org.apache.pekko.actor.Identify" = pekko-misc
       "org.apache.pekko.actor.ActorIdentity" = pekko-misc
diff --git 
a/remote/src/test/scala/org/apache/pekko/remote/serialization/ProtobufSerializerSpec.scala
 
b/remote/src/test/scala/org/apache/pekko/remote/serialization/ProtobufSerializerSpec.scala
index 9d79c066bc..1a7146b0b4 100644
--- 
a/remote/src/test/scala/org/apache/pekko/remote/serialization/ProtobufSerializerSpec.scala
+++ 
b/remote/src/test/scala/org/apache/pekko/remote/serialization/ProtobufSerializerSpec.scala
@@ -91,6 +91,14 @@ class ProtobufSerializerSpec extends PekkoSpec(s"""
       ser.serializerFor(classOf[MyMessageV3]).getClass should 
===(classOf[ProtobufSerializer])
     }
 
+    "skip the optional ScalaPB binding when ScalaPB is not on the classpath" 
in {
+      val scalaPbClassName = "scalapb.GeneratedMessage"
+      ser.settings.SerializationBindings.get(scalaPbClassName) should 
===(Some("proto"))
+      val dynamicAccess = 
system.asInstanceOf[ExtendedActorSystem].dynamicAccess
+      dynamicAccess.getClassFor[Any](scalaPbClassName).isFailure should 
===(true)
+      ser.bindings.exists { case (clazz, _) => clazz.getName == 
scalaPbClassName } should ===(false)
+    }
+
     "work for SerializedMessage (just an org.apache.pekko.protobuf message)" 
in {
       // create a protobuf message
       val protobufMessage: SerializedMessage =


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

Reply via email to