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

pjfanning 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 198f936f29 Support ActorSystem/ClassicActorSystemProvider serializer 
constructors, improve error message (#2948)
198f936f29 is described below

commit 198f936f29241bf763e5e0b2e07002c0a98f6bd8
Author: PJ Fanning <[email protected]>
AuthorDate: Sat May 9 09:17:30 2026 +0100

    Support ActorSystem/ClassicActorSystemProvider serializer constructors, 
improve error message (#2948)
    
    * Support ActorSystem/ClassicActorSystemProvider serializer constructors, 
improve error message
    
    Motivation:
    When cascading down over all constructor shapes and failing to find any,
    the user gets a NoSuchMethodException for ClassicActorSystemProvider and
    String, giving the impression that this is the only supported shape.
    
    Modification:
    - serializerOf now tries (ExtendedActorSystem), (ActorSystem),
      (ClassicActorSystemProvider), () and then the same with a String
      binding name argument.
    - On total failure, an informative message lists all supported shapes.
    - Added ConstructorSerializer test classes and a 'look for various
      constructors' test in SerializeSpec.
    
    Result:
    Serializers with ActorSystem or ClassicActorSystemProvider constructors
    are now accepted, and failures show a clear list of supported shapes.
    
    Tests:
    - sbt 'actor-tests / Test / testOnly 
org.apache.pekko.serialization.SerializeSpec'
      All 18 tests passed.
    
    References:
    Refs https://github.com/akka/akka-core/pull/31936
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko/sessions/8f6a60a1-5266-49d5-ada7-0553fab4ad4f
    
    Co-authored-by: pjfanning <[email protected]>
    
    * Fix error message order to match actual constructor try order
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko/sessions/8f6a60a1-5266-49d5-ada7-0553fab4ad4f
    
    Co-authored-by: pjfanning <[email protected]>
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] 
<[email protected]>
    Co-authored-by: pjfanning <[email protected]>
---
 .../apache/pekko/serialization/SerializeSpec.scala | 70 ++++++++++++++++++++++
 .../apache/pekko/serialization/Serialization.scala | 46 ++++++++++----
 2 files changed, 105 insertions(+), 11 deletions(-)

diff --git 
a/actor-tests/src/test/scala/org/apache/pekko/serialization/SerializeSpec.scala 
b/actor-tests/src/test/scala/org/apache/pekko/serialization/SerializeSpec.scala
index ba06386493..22c4321bdd 100644
--- 
a/actor-tests/src/test/scala/org/apache/pekko/serialization/SerializeSpec.scala
+++ 
b/actor-tests/src/test/scala/org/apache/pekko/serialization/SerializeSpec.scala
@@ -43,6 +43,13 @@ object SerializationTests {
           test = "org.apache.pekko.serialization.NoopSerializer"
           test2 = "org.apache.pekko.serialization.NoopSerializer2"
           other = "other.SerializerOutsidePekkoPackage"
+          constructor1 = 
"org.apache.pekko.serialization.Constructor1Serializer"
+          constructor2 = 
"org.apache.pekko.serialization.Constructor2Serializer"
+          constructor3 = 
"org.apache.pekko.serialization.Constructor3Serializer"
+          constructor4 = 
"org.apache.pekko.serialization.Constructor4Serializer"
+          constructor5 = 
"org.apache.pekko.serialization.Constructor5Serializer"
+          constructor6 = 
"org.apache.pekko.serialization.Constructor6Serializer"
+          constructor7 = 
"org.apache.pekko.serialization.Constructor7Serializer"
         }
 
         serialization-bindings {
@@ -55,6 +62,13 @@ object SerializationTests {
           "org.apache.pekko.serialization.SerializationTests$$D" = test
           "org.apache.pekko.serialization.SerializationTests$$Marker2" = test2
           "org.apache.pekko.serialization.SerializationTests$$AbstractOther" = 
other
+          "org.apache.pekko.serialization.ConstructorSerializer$$No1" = 
constructor1
+          "org.apache.pekko.serialization.ConstructorSerializer$$No2" = 
constructor2
+          "org.apache.pekko.serialization.ConstructorSerializer$$No3" = 
constructor3
+          "org.apache.pekko.serialization.ConstructorSerializer$$No4" = 
constructor4
+          "org.apache.pekko.serialization.ConstructorSerializer$$No5" = 
constructor5
+          "org.apache.pekko.serialization.ConstructorSerializer$$No6" = 
constructor6
+          "org.apache.pekko.serialization.ConstructorSerializer$$No7" = 
constructor7
         }
       }
     }
@@ -303,6 +317,16 @@ class SerializeSpec extends 
PekkoSpec(SerializationTests.serializeConf) {
         shutdown(sys)
       }.getMessage should include).regex("Serializer identifier \\[9999\\].*is 
not unique")
     }
+
+    "look for various constructors" in {
+      ser.serializerFor(classOf[ConstructorSerializer.No1]).getClass should 
===(classOf[Constructor1Serializer])
+      ser.serializerFor(classOf[ConstructorSerializer.No2]).getClass should 
===(classOf[Constructor2Serializer])
+      ser.serializerFor(classOf[ConstructorSerializer.No3]).getClass should 
===(classOf[Constructor3Serializer])
+      ser.serializerFor(classOf[ConstructorSerializer.No4]).getClass should 
===(classOf[Constructor4Serializer])
+      ser.serializerFor(classOf[ConstructorSerializer.No5]).getClass should 
===(classOf[Constructor5Serializer])
+      ser.serializerFor(classOf[ConstructorSerializer.No6]).getClass should 
===(classOf[Constructor6Serializer])
+      ser.serializerFor(classOf[ConstructorSerializer.No7]).getClass should 
===(classOf[Constructor7Serializer])
+    }
   }
 }
 
@@ -631,3 +655,49 @@ class DeadlockSerializer(system: ExtendedActorSystem) 
extends Serializer {
 
   def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = null
 }
+
+object ConstructorSerializer {
+  class No1
+  class No2
+  class No3
+  class No4
+  class No5
+  class No6
+  class No7
+}
+
+private[pekko] abstract class ConstructorSerializer extends 
SerializerWithStringManifest {
+
+  def toBinary(o: AnyRef): Array[Byte] =
+    Array.empty[Byte]
+
+  override def manifest(o: AnyRef): String = "test"
+
+  override def fromBinary(bytes: Array[Byte], manifest: String): AnyRef = 
"Test"
+}
+
+private[pekko] class Constructor1Serializer(@nowarn("msg=never used") system: 
ExtendedActorSystem)
+    extends ConstructorSerializer { override def identifier = 100001 }
+
+private[pekko] class Constructor2Serializer(@nowarn("msg=never used") system: 
ActorSystem)
+    extends ConstructorSerializer { override def identifier = 100002 }
+
+private[pekko] class Constructor3Serializer(@nowarn("msg=never used") system: 
ClassicActorSystemProvider)
+    extends ConstructorSerializer { override def identifier = 100003 }
+
+private[pekko] class Constructor4Serializer extends ConstructorSerializer { 
override def identifier = 100004 }
+
+private[pekko] class Constructor5Serializer(
+    @nowarn("msg=never used") system: ExtendedActorSystem,
+    @nowarn("msg=never used") binding: String)
+    extends ConstructorSerializer { override def identifier = 100005 }
+
+private[pekko] class Constructor6Serializer(
+    @nowarn("msg=never used") system: ActorSystem,
+    @nowarn("msg=never used") binding: String)
+    extends ConstructorSerializer { override def identifier = 100006 }
+
+private[pekko] class Constructor7Serializer(
+    @nowarn("msg=never used") system: ClassicActorSystemProvider,
+    @nowarn("msg=never used") binding: String)
+    extends ConstructorSerializer { override def identifier = 100007 }
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 690ab5fabb..eb878635ec 100644
--- a/actor/src/main/scala/org/apache/pekko/serialization/Serialization.scala
+++ b/actor/src/main/scala/org/apache/pekko/serialization/Serialization.scala
@@ -379,17 +379,41 @@ class Serialization(val system: ExtendedActorSystem) 
extends Extension {
         classOf[DisabledJavaSerializer].getName
       } else serializerFQN
 
-    system.dynamicAccess.createInstanceFor[Serializer](fqn, 
List(classOf[ExtendedActorSystem] -> system)).recoverWith {
-      case _: NoSuchMethodException =>
-        system.dynamicAccess.createInstanceFor[Serializer](fqn, 
Nil).recoverWith {
-          case e: NoSuchMethodException =>
-            if (bindingName == "") throw e // compatibility with (public) 
serializerOf method without bindingName
-            else
-              system.dynamicAccess.createInstanceFor[Serializer](
-                fqn,
-                List(classOf[ExtendedActorSystem] -> system, classOf[String] 
-> bindingName))
-        }
-    }
+    // Try constructors in order of preference (most specific first)
+    val singleArgConstructors: List[List[(Class[_], AnyRef)]] =
+      List(
+        List(classOf[ExtendedActorSystem] -> system),
+        List(classOf[ActorSystem] -> system),
+        List(classOf[ClassicActorSystemProvider] -> (system: 
ClassicActorSystemProvider)),
+        Nil)
+
+    val twoArgConstructors: List[List[(Class[_], AnyRef)]] =
+      if (bindingName == "") Nil
+      else
+        List(
+          List(classOf[ExtendedActorSystem] -> system, classOf[String] -> 
bindingName),
+          List(classOf[ActorSystem] -> system, classOf[String] -> bindingName),
+          List(classOf[ClassicActorSystemProvider] -> (system: 
ClassicActorSystemProvider),
+            classOf[String] -> bindingName))
+
+    val allConstructors = singleArgConstructors ++ twoArgConstructors
+
+    def tryNext(remaining: List[List[(Class[_], AnyRef)]]): Try[Serializer] =
+      remaining match {
+        case Nil =>
+          Failure(
+            new NoSuchMethodException(
+              s"None of the supported constructors were found for serializer 
[$fqn]. " +
+              s"Supported constructor shapes (tried in order): " +
+              s"(ExtendedActorSystem), (ActorSystem), 
(ClassicActorSystemProvider), (), " +
+              s"(ExtendedActorSystem, String), (ActorSystem, String), 
(ClassicActorSystemProvider, String)."))
+        case args :: rest =>
+          system.dynamicAccess.createInstanceFor[Serializer](fqn, 
args).recoverWith {
+            case _: NoSuchMethodException => tryNext(rest)
+          }
+      }
+
+    tryNext(allConstructors)
   }
 
   /**


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

Reply via email to