diff --git a/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala b/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala
index 7b91e29bcb..9ab93ed974 100644
--- a/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala
+++ b/akka-actor-tests/src/test/scala/akka/serialization/SerializeSpec.scala
@@ -179,14 +179,10 @@ class SerializeSpec extends AkkaSpec(SerializeSpec.config) {
ser.serializerFor(classOf[ExtendedPlainMessage]).getClass must be(classOf[TestSerializer])
}
- "resolve serializer for message with several bindings" in {
- ser.serializerFor(classOf[Both]).getClass must be(classOf[TestSerializer])
- }
-
- "resolve serializer in the order of the bindings" in {
- ser.serializerFor(classOf[A]).getClass must be(classOf[JavaSerializer])
- ser.serializerFor(classOf[B]).getClass must be(classOf[TestSerializer])
- ser.serializerFor(classOf[C]).getClass must be(classOf[JavaSerializer])
+ "throw exception for message with several bindings" in {
+ intercept[java.io.NotSerializableException] {
+ ser.serializerFor(classOf[Both])
+ }
}
"resolve serializer in the order of most specific binding first" in {
diff --git a/akka-actor/src/main/resources/reference.conf b/akka-actor/src/main/resources/reference.conf
index fb0ad24a75..4e6c42c704 100644
--- a/akka-actor/src/main/resources/reference.conf
+++ b/akka-actor/src/main/resources/reference.conf
@@ -269,15 +269,16 @@ akka {
# Entries for pluggable serializers and their bindings.
serializers {
java = "akka.serialization.JavaSerializer"
- # proto = "akka.serialization.ProtobufSerializer"
}
# Class to Serializer binding. You only need to specify the name of an interface
- # or abstract base class of the messages. In case of ambiguity it is primarily
- # using the most specific configured class, and secondly the entry configured first.
+ # or abstract base class of the messages. In case of ambiguity it is
+ # using the most specific configured class, throwing an exception otherwise.
+ #
+ # To disable one of the default serializers, assign its class to "none", like
+ # "java.io.Serializable" = none
serialization-bindings {
"java.io.Serializable" = java
- #"com.google.protobuf.Message" = proto
}
}
diff --git a/akka-actor/src/main/scala/akka/serialization/Serialization.scala b/akka-actor/src/main/scala/akka/serialization/Serialization.scala
index 1a23833383..3e86de6c1c 100644
--- a/akka-actor/src/main/scala/akka/serialization/Serialization.scala
+++ b/akka-actor/src/main/scala/akka/serialization/Serialization.scala
@@ -125,17 +125,23 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
def serializerFor(clazz: Class[_]): Serializer =
serializerMap.get(clazz) match {
case null ⇒
- val ser = bindings.collectFirst {
- case (c, s) if c.isAssignableFrom(clazz) ⇒ s
- } getOrElse (throw new NotSerializableException(
- "No configured serialization-bindings for class [%s]" format clazz.getName))
+ // bindings are ordered from most specific to least specific
+ def unique(cs: Seq[Class[_]], ser: Set[Serializer]): Boolean = (cs forall (_ isAssignableFrom cs(0))) || ser.size == 1
- // memorize for performance
- serializerMap.putIfAbsent(clazz, ser) match {
- case null ⇒
- log.debug("Using serializer[{}] for message [{}]", ser.getClass.getName, clazz.getName)
- ser
- case some ⇒ some
+ val possible = bindings filter { _._1 isAssignableFrom clazz }
+ possible.size match {
+ case 0 ⇒
+ throw new NotSerializableException("No configured serialization-bindings for class [%s]" format clazz.getName)
+ case x if x == 1 || unique(possible map (_._1), possible.map(_._2)(scala.collection.breakOut)) ⇒
+ val ser = possible(0)._2
+ serializerMap.putIfAbsent(clazz, ser) match {
+ case null ⇒
+ log.debug("Using serializer[{}] for message [{}]", ser.getClass.getName, clazz.getName)
+ ser
+ case some ⇒ some
+ }
+ case _ ⇒
+ throw new NotSerializableException("Multiple serializers found for " + clazz + ": " + possible)
}
case ser ⇒ ser
}
@@ -160,7 +166,7 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
* It is primarily ordered by the most specific classes first, and secondly in the configured order.
*/
private[akka] val bindings: Seq[ClassSerializer] = {
- val configuredBindings = for ((k: String, v: String) ← settings.SerializationBindings) yield {
+ val configuredBindings = for ((k: String, v: String) ← settings.SerializationBindings if v != "none") yield {
val c = ReflectiveAccess.getClassFor(k, system.internalClassLoader).fold(throw _, identity[Class[_]])
(c, serializers(v))
}
diff --git a/akka-docs/java/serialization.rst b/akka-docs/java/serialization.rst
index b0721e82cc..dad2174f91 100644
--- a/akka-docs/java/serialization.rst
+++ b/akka-docs/java/serialization.rst
@@ -32,14 +32,26 @@ should be serialized using which ``Serializer``, this is done in the "akka.actor
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
-You only need to specify the name of an interface or abstract base class of the messages. In case of ambiguity,
-i.e. the message implements several of the configured classes, it is primarily using the most specific
-configured class, and secondly the entry configured first.
+You only need to specify the name of an interface or abstract base class of the
+messages. In case of ambiguity, i.e. the message implements several of the
+configured classes, the most specific configured class will be used, i.e. the
+one of which all other candidates are superclasses. If this condition cannot be
+met, because e.g. ``java.io.Serializable`` and ``MyOwnSerializable`` both apply
+and neither is a subtype of the other, an exception will be thrown during
+serialization.
-Akka provides serializers for ``java.io.Serializable`` and `protobuf `_
-``com.google.protobuf.Message`` by default, so normally you don't need to add configuration for that, but
-it can be done to force a specific serializer in case messages implements both ``java.io.Serializable``
-and ``com.google.protobuf.Message``.
+Akka provides serializers for :class:`java.io.Serializable` and `protobuf
+`_
+:class:`com.google.protobuf.GeneratedMessage` by default (the latter only if
+depending on the akka-remote module), so normally you don't need to add
+configuration for that; since :class:`com.google.protobuf.GeneratedMessage`
+implements :class:`java.io.Serializable`, protobuf messages will always by
+serialized using the protobuf protocol unless specifically overridden. In order
+to disable a default serializer, map its marker type to “none”::
+
+ akka.actor.serialization-bindings {
+ "java.io.Serializable" = none
+ }
Verification
------------
@@ -91,4 +103,4 @@ which is done by extending ``akka.serialization.JSerializer``, like this:
:exclude: ...
Then you only need to fill in the blanks, bind it to a name in your :ref:`configuration` and then
-list which classes that should be serialized using it.
\ No newline at end of file
+list which classes that should be serialized using it.
diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst
index 9cfaf909b9..ec0f4ca706 100644
--- a/akka-docs/scala/serialization.rst
+++ b/akka-docs/scala/serialization.rst
@@ -32,14 +32,26 @@ should be serialized using which ``Serializer``, this is done in the "akka.actor
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
-You only need to specify the name of an interface or abstract base class of the messages. In case of ambiguity,
-i.e. the message implements several of the configured classes, it is primarily using the most specific
-configured class, and secondly the entry configured first.
+You only need to specify the name of an interface or abstract base class of the
+messages. In case of ambiguity, i.e. the message implements several of the
+configured classes, the most specific configured class will be used, i.e. the
+one of which all other candidates are superclasses. If this condition cannot be
+met, because e.g. ``java.io.Serializable`` and ``MyOwnSerializable`` both apply
+and neither is a subtype of the other, an exception will be thrown during
+serialization.
-Akka provides serializers for ``java.io.Serializable`` and `protobuf `_
-``com.google.protobuf.Message`` by default, so normally you don't need to add configuration for that, but
-it can be done to force a specific serializer in case messages implements both ``java.io.Serializable``
-and ``com.google.protobuf.Message``.
+Akka provides serializers for :class:`java.io.Serializable` and `protobuf
+`_
+:class:`com.google.protobuf.GeneratedMessage` by default (the latter only if
+depending on the akka-remote module), so normally you don't need to add
+configuration for that; since :class:`com.google.protobuf.GeneratedMessage`
+implements :class:`java.io.Serializable`, protobuf messages will always by
+serialized using the protobuf protocol unless specifically overridden. In order
+to disable a default serializer, map its marker type to “none”::
+
+ akka.actor.serialization-bindings {
+ "java.io.Serializable" = none
+ }
Verification
------------
@@ -89,4 +101,4 @@ First you need to create a class definition of your ``Serializer`` like so:
:exclude: ...
Then you only need to fill in the blanks, bind it to a name in your :ref:`configuration` and then
-list which classes that should be serialized using it.
\ No newline at end of file
+list which classes that should be serialized using it.
diff --git a/akka-remote/src/main/resources/reference.conf b/akka-remote/src/main/resources/reference.conf
index 38e0de27cc..a8854fc9df 100644
--- a/akka-remote/src/main/resources/reference.conf
+++ b/akka-remote/src/main/resources/reference.conf
@@ -11,17 +11,18 @@ akka {
# Entries for pluggable serializers and their bindings.
serializers {
- java = "akka.serialization.JavaSerializer"
proto = "akka.serialization.ProtobufSerializer"
}
# Class to Serializer binding. You only need to specify the name of an interface
- # or abstract base class of the messages. In case of ambiguity it is primarily
- # using the most specific configured class, and secondly the entry configured first.
+ # or abstract base class of the messages. In case of ambiguity it is
+ # using the most specific configured class, giving an error if two mappings are found
+ # which cannot be decided by sub-typing relation.
serialization-bindings {
- "com.google.protobuf.Message" = proto
- "java.io.Serializable" = java
+ # Since com.google.protobuf.Message does not extend Serializable but GeneratedMessage
+ # does, need to use the more specific one here in order to avoid ambiguity
+ "com.google.protobuf.GeneratedMessage" = proto
}
deployment {