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]