Merge pull request #309 from jboner/wip-1789-ser2-patriknw
Configure serializer with class as key. See #1789
This commit is contained in:
commit
50d107e150
9 changed files with 204 additions and 242 deletions
|
|
@ -5,7 +5,6 @@
|
||||||
package akka.serialization
|
package akka.serialization
|
||||||
|
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import java.io._
|
import java.io._
|
||||||
import akka.dispatch.Await
|
import akka.dispatch.Await
|
||||||
|
|
@ -17,21 +16,25 @@ import akka.pattern.ask
|
||||||
|
|
||||||
object SerializeSpec {
|
object SerializeSpec {
|
||||||
|
|
||||||
val serializationConf = ConfigFactory.parseString("""
|
val config = """
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serializers {
|
serializers {
|
||||||
java = "akka.serialization.JavaSerializer"
|
|
||||||
test = "akka.serialization.TestSerializer"
|
test = "akka.serialization.TestSerializer"
|
||||||
}
|
}
|
||||||
|
|
||||||
serialization-bindings {
|
serialization-bindings {
|
||||||
java = ["akka.serialization.SerializeSpec$Person", "akka.serialization.SerializeSpec$Address", "akka.serialization.MyJavaSerializableActor", "akka.serialization.MyStatelessActorWithMessagesInMailbox", "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
"akka.serialization.SerializeSpec$Person" = java
|
||||||
test = ["akka.serialization.TestSerializble", "akka.serialization.SerializeSpec$PlainMessage"]
|
"akka.serialization.SerializeSpec$Address" = java
|
||||||
|
"akka.serialization.TestSerializble" = test
|
||||||
|
"akka.serialization.SerializeSpec$PlainMessage" = test
|
||||||
|
"akka.serialization.SerializeSpec$A" = java
|
||||||
|
"akka.serialization.SerializeSpec$B" = test
|
||||||
|
"akka.serialization.SerializeSpec$D" = test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
"""
|
||||||
|
|
||||||
@BeanInfo
|
@BeanInfo
|
||||||
case class Address(no: String, street: String, city: String, zip: String) { def this() = this("", "", "", "") }
|
case class Address(no: String, street: String, city: String, zip: String) { def this() = this("", "", "", "") }
|
||||||
|
|
@ -54,10 +57,18 @@ object SerializeSpec {
|
||||||
|
|
||||||
class ExtendedPlainMessage extends PlainMessage
|
class ExtendedPlainMessage extends PlainMessage
|
||||||
|
|
||||||
|
class Both(s: String) extends SimpleMessage(s) with Serializable
|
||||||
|
|
||||||
|
trait A
|
||||||
|
trait B
|
||||||
|
class C extends B with A
|
||||||
|
class D extends A
|
||||||
|
class E extends D
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class SerializeSpec extends AkkaSpec(SerializeSpec.serializationConf) {
|
class SerializeSpec extends AkkaSpec(SerializeSpec.config) {
|
||||||
import SerializeSpec._
|
import SerializeSpec._
|
||||||
|
|
||||||
val ser = SerializationExtension(system)
|
val ser = SerializationExtension(system)
|
||||||
|
|
@ -69,8 +80,8 @@ class SerializeSpec extends AkkaSpec(SerializeSpec.serializationConf) {
|
||||||
"Serialization" must {
|
"Serialization" must {
|
||||||
|
|
||||||
"have correct bindings" in {
|
"have correct bindings" in {
|
||||||
ser.bindings(addr.getClass.getName) must be("java")
|
ser.bindings.collectFirst { case (c, s) if c == addr.getClass ⇒ s.getClass } must be(Some(classOf[JavaSerializer]))
|
||||||
ser.bindings(classOf[PlainMessage].getName) must be("test")
|
ser.bindings.collectFirst { case (c, s) if c == classOf[PlainMessage] ⇒ s.getClass } must be(Some(classOf[TestSerializer]))
|
||||||
}
|
}
|
||||||
|
|
||||||
"serialize Address" in {
|
"serialize Address" in {
|
||||||
|
|
@ -144,58 +155,64 @@ class SerializeSpec extends AkkaSpec(SerializeSpec.serializationConf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by direct interface" in {
|
"resolve serializer by direct interface" in {
|
||||||
val msg = new SimpleMessage("foo")
|
ser.serializerFor(classOf[SimpleMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by interface implemented by super class" in {
|
"resolve serializer by interface implemented by super class" in {
|
||||||
val msg = new ExtendedSimpleMessage("foo", 17)
|
ser.serializerFor(classOf[ExtendedSimpleMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by indirect interface" in {
|
"resolve serializer by indirect interface" in {
|
||||||
val msg = new AnotherMessage
|
ser.serializerFor(classOf[AnotherMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by indirect interface implemented by super class" in {
|
"resolve serializer by indirect interface implemented by super class" in {
|
||||||
val msg = new ExtendedAnotherMessage
|
ser.serializerFor(classOf[ExtendedAnotherMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer for message with binding" in {
|
"resolve serializer for message with binding" in {
|
||||||
val msg = new PlainMessage
|
ser.serializerFor(classOf[PlainMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer for message extending class with with binding" in {
|
"resolve serializer for message extending class with with binding" in {
|
||||||
val msg = new ExtendedPlainMessage
|
ser.serializerFor(classOf[ExtendedPlainMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).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])
|
||||||
|
}
|
||||||
|
|
||||||
|
"resolve serializer in the order of most specific binding first" in {
|
||||||
|
ser.serializerFor(classOf[A]).getClass must be(classOf[JavaSerializer])
|
||||||
|
ser.serializerFor(classOf[D]).getClass must be(classOf[TestSerializer])
|
||||||
|
ser.serializerFor(classOf[E]).getClass must be(classOf[TestSerializer])
|
||||||
|
}
|
||||||
|
|
||||||
|
"throw java.io.NotSerializableException when no binding" in {
|
||||||
|
intercept[java.io.NotSerializableException] {
|
||||||
|
ser.serializerFor(classOf[Actor])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object VerifySerializabilitySpec {
|
object VerifySerializabilitySpec {
|
||||||
val conf = ConfigFactory.parseString("""
|
val conf = """
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serialize-messages = on
|
serialize-messages = on
|
||||||
|
|
||||||
serialize-creators = on
|
serialize-creators = on
|
||||||
|
|
||||||
serializers {
|
|
||||||
java = "akka.serialization.JavaSerializer"
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
java = ["akka.serialization.SerializeSpec$Address", "akka.serialization.MyJavaSerializableActor", "akka.serialization.MyStatelessActorWithMessagesInMailbox", "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
"""
|
||||||
|
|
||||||
class FooActor extends Actor {
|
class FooActor extends Actor {
|
||||||
def receive = {
|
def receive = {
|
||||||
|
|
@ -210,6 +227,7 @@ object VerifySerializabilitySpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class VerifySerializabilitySpec extends AkkaSpec(VerifySerializabilitySpec.conf) {
|
class VerifySerializabilitySpec extends AkkaSpec(VerifySerializabilitySpec.conf) {
|
||||||
import VerifySerializabilitySpec._
|
import VerifySerializabilitySpec._
|
||||||
implicit val timeout = Timeout(5 seconds)
|
implicit val timeout = Timeout(5 seconds)
|
||||||
|
|
|
||||||
|
|
@ -266,23 +266,19 @@ akka {
|
||||||
event-stream = off
|
event-stream = off
|
||||||
}
|
}
|
||||||
|
|
||||||
# Entries for pluggable serializers and their bindings. If a binding for a specific
|
# Entries for pluggable serializers and their bindings.
|
||||||
# class is not found, then the default serializer (Java serialization) is used.
|
|
||||||
serializers {
|
serializers {
|
||||||
# java = "akka.serialization.JavaSerializer"
|
java = "akka.serialization.JavaSerializer"
|
||||||
# proto = "akka.serialization.ProtobufSerializer"
|
# proto = "akka.serialization.ProtobufSerializer"
|
||||||
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# serialization-bindings {
|
# Class to Serializer binding. You only need to specify the name of an interface
|
||||||
# java = ["akka.serialization.SerializeSpec$Address",
|
# or abstract base class of the messages. In case of ambiguity it is primarily
|
||||||
# "akka.serialization.MyJavaSerializableActor",
|
# using the most specific configured class, and secondly the entry configured first.
|
||||||
# "akka.serialization.MyStatelessActorWithMessagesInMailbox",
|
serialization-bindings {
|
||||||
# "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
"java.io.Serializable" = java
|
||||||
# proto = ["com.google.protobuf.Message",
|
#"com.google.protobuf.Message" = proto
|
||||||
# "akka.actor.ProtobufProtocol$MyMessage"]
|
}
|
||||||
# }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Used to set the behavior of the scheduler.
|
# Used to set the behavior of the scheduler.
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,21 @@ import akka.AkkaException
|
||||||
import akka.util.ReflectiveAccess
|
import akka.util.ReflectiveAccess
|
||||||
import scala.util.DynamicVariable
|
import scala.util.DynamicVariable
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import akka.config.ConfigurationException
|
|
||||||
import akka.actor.{ Extension, ActorSystem, ExtendedActorSystem, Address }
|
import akka.actor.{ Extension, ActorSystem, ExtendedActorSystem, Address }
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
import java.io.NotSerializableException
|
||||||
|
|
||||||
case class NoSerializerFoundException(m: String) extends AkkaException(m)
|
case class NoSerializerFoundException(m: String) extends AkkaException(m)
|
||||||
|
|
||||||
object Serialization {
|
object Serialization {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuple that represents mapping from Class to Serializer
|
||||||
|
*/
|
||||||
|
type ClassSerializer = (Class[_], Serializer)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This holds a reference to the current ActorSystem (the surrounding context)
|
* This holds a reference to the current ActorSystem (the surrounding context)
|
||||||
* during serialization and deserialization.
|
* during serialization and deserialization.
|
||||||
|
|
@ -40,28 +47,19 @@ object Serialization {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
val Serializers: Map[String, String] =
|
val Serializers: Map[String, String] = configToMap(getConfig("akka.actor.serializers"))
|
||||||
getConfig("akka.actor.serializers").root.unwrapped.asScala.toMap.map { case (k, v) ⇒ (k, v.toString) }
|
|
||||||
|
|
||||||
val SerializationBindings: Map[String, Seq[String]] = {
|
val SerializationBindings: Map[String, String] = configToMap(getConfig("akka.actor.serialization-bindings"))
|
||||||
val configPath = "akka.actor.serialization-bindings"
|
|
||||||
hasPath(configPath) match {
|
private def configToMap(cfg: Config): Map[String, String] =
|
||||||
case false ⇒ Map()
|
cfg.root.unwrapped.asScala.toMap.map { case (k, v) ⇒ (k, v.toString) }
|
||||||
case true ⇒
|
|
||||||
val serializationBindings: Map[String, Seq[String]] = getConfig(configPath).root.unwrapped.asScala.toMap.map {
|
|
||||||
case (k: String, v: java.util.Collection[_]) ⇒ (k -> v.asScala.toSeq.asInstanceOf[Seq[String]])
|
|
||||||
case invalid ⇒ throw new ConfigurationException("Invalid serialization-bindings [%s]".format(invalid))
|
|
||||||
}
|
|
||||||
serializationBindings
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization module. Contains methods for serialization and deserialization as well as
|
* Serialization module. Contains methods for serialization and deserialization as well as
|
||||||
* locating a Serializer for a particular class as defined in the mapping in the 'akka.conf' file.
|
* locating a Serializer for a particular class as defined in the mapping in the configuration.
|
||||||
*/
|
*/
|
||||||
class Serialization(val system: ExtendedActorSystem) extends Extension {
|
class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
import Serialization._
|
import Serialization._
|
||||||
|
|
@ -105,8 +103,10 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
} catch { case e: Exception ⇒ Left(e) }
|
} catch { case e: Exception ⇒ Left(e) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Serializer configured for the given object, returns the NullSerializer if it's null,
|
* Returns the Serializer configured for the given object, returns the NullSerializer if it's null.
|
||||||
* falls back to the Serializer named "default"
|
*
|
||||||
|
* @throws akka.config.ConfigurationException if no `serialization-bindings` is configured for the
|
||||||
|
* class of the object
|
||||||
*/
|
*/
|
||||||
def findSerializerFor(o: AnyRef): Serializer = o match {
|
def findSerializerFor(o: AnyRef): Serializer = o match {
|
||||||
case null ⇒ NullSerializer
|
case null ⇒ NullSerializer
|
||||||
|
|
@ -114,82 +114,86 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the configured Serializer for the given Class, falls back to the Serializer named "default".
|
* Returns the configured Serializer for the given Class. The configured Serializer
|
||||||
* It traverses interfaces and super classes to find any configured Serializer that match
|
* is used if the configured class `isAssignableFrom` from the `clazz`, i.e.
|
||||||
* the class name.
|
* the configured class is a super class or implemented interface. In case of
|
||||||
|
* ambiguity it is primarily using the most specific configured class,
|
||||||
|
* and secondly the entry configured first.
|
||||||
|
*
|
||||||
|
* @throws java.io.NotSerializableException if no `serialization-bindings` is configured for the class
|
||||||
*/
|
*/
|
||||||
def serializerFor(clazz: Class[_]): Serializer =
|
def serializerFor(clazz: Class[_]): Serializer =
|
||||||
if (bindings.isEmpty) {
|
serializerMap.get(clazz) match {
|
||||||
// quick path to default when no bindings are registered
|
case null ⇒
|
||||||
serializers("default")
|
val ser = bindings.collectFirst {
|
||||||
} else {
|
case (c, s) if c.isAssignableFrom(clazz) ⇒ s
|
||||||
|
} getOrElse (throw new NotSerializableException(
|
||||||
|
"No configured serialization-bindings for class [%s]" format clazz.getName))
|
||||||
|
|
||||||
def resolve(c: Class[_]): Option[Serializer] =
|
// memorize for performance
|
||||||
serializerMap.get(c.getName) match {
|
serializerMap.putIfAbsent(clazz, ser) match {
|
||||||
case null ⇒
|
case null ⇒
|
||||||
val classes = c.getInterfaces ++ Option(c.getSuperclass)
|
log.debug("Using serializer[{}] for message [{}]", ser.getClass.getName, clazz.getName)
|
||||||
classes.view map resolve collectFirst { case Some(x) ⇒ x }
|
ser
|
||||||
case x ⇒ Some(x)
|
case some ⇒ some
|
||||||
}
|
}
|
||||||
|
case ser ⇒ ser
|
||||||
serializerMap.get(clazz.getName) match {
|
|
||||||
case null ⇒
|
|
||||||
val ser = resolve(clazz).getOrElse(serializers("default"))
|
|
||||||
// memorize the lookups for performance
|
|
||||||
serializerMap.putIfAbsent(clazz.getName, ser) match {
|
|
||||||
case null ⇒
|
|
||||||
log.debug("Using serializer[{}] for message [{}]", ser.getClass.getName, clazz.getName)
|
|
||||||
ser
|
|
||||||
case some ⇒ some
|
|
||||||
}
|
|
||||||
case ser ⇒ ser
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to load the specified Serializer by the FQN
|
* Tries to instantiate the specified Serializer by the FQN
|
||||||
*/
|
*/
|
||||||
def serializerOf(serializerFQN: String): Either[Exception, Serializer] =
|
def serializerOf(serializerFQN: String): Either[Exception, Serializer] =
|
||||||
ReflectiveAccess.createInstance(serializerFQN, ReflectiveAccess.noParams, ReflectiveAccess.noArgs, system.internalClassLoader)
|
ReflectiveAccess.createInstance(serializerFQN, ReflectiveAccess.noParams, ReflectiveAccess.noArgs, system.internalClassLoader)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Map of serializer from alias to implementation (class implementing akka.serialization.Serializer)
|
* A Map of serializer from alias to implementation (class implementing akka.serialization.Serializer)
|
||||||
* By default always contains the following mapping: "default" -> akka.serialization.JavaSerializer
|
* By default always contains the following mapping: "java" -> akka.serialization.JavaSerializer
|
||||||
* But "default" can be overridden in config
|
|
||||||
*/
|
*/
|
||||||
lazy val serializers: Map[String, Serializer] = {
|
private val serializers: Map[String, Serializer] = {
|
||||||
val serializersConf = settings.Serializers
|
for ((k: String, v: String) ← settings.Serializers)
|
||||||
for ((k: String, v: String) ← serializersConf)
|
|
||||||
yield k -> serializerOf(v).fold(throw _, identity)
|
yield k -> serializerOf(v).fold(throw _, identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bindings is a Map whose keys = FQN of class that is serializable and values = the alias of the serializer to be used
|
* bindings is a Seq of tuple representing the mapping from Class to Serializer.
|
||||||
|
* It is primarily ordered by the most specific classes first, and secondly in the configured order.
|
||||||
*/
|
*/
|
||||||
lazy val bindings: Map[String, String] = {
|
private[akka] val bindings: Seq[ClassSerializer] = {
|
||||||
settings.SerializationBindings.foldLeft(Map[String, String]()) {
|
val configuredBindings = for ((k: String, v: String) ← settings.SerializationBindings) yield {
|
||||||
//All keys which are lists, take the Strings from them and Map them
|
val c = ReflectiveAccess.getClassFor(k, system.internalClassLoader).fold(throw _, identity[Class[_]])
|
||||||
case (result, (k: String, vs: Seq[_])) ⇒ result ++ (vs collect { case v: String ⇒ (v, k) })
|
(c, serializers(v))
|
||||||
//For any other values, just skip them
|
|
||||||
case (result, _) ⇒ result
|
|
||||||
}
|
}
|
||||||
|
sort(configuredBindings)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* serializerMap is a Map whose keys = FQN of class that is serializable and values is the serializer to be used for that class
|
* Sort so that subtypes always precede their supertypes, but without
|
||||||
|
* obeying any order between unrelated subtypes (insert sort).
|
||||||
*/
|
*/
|
||||||
private lazy val serializerMap: ConcurrentHashMap[String, Serializer] = {
|
private def sort(in: Iterable[ClassSerializer]): Seq[ClassSerializer] =
|
||||||
val serializerMap = new ConcurrentHashMap[String, Serializer]
|
(new ArrayBuffer[ClassSerializer](in.size) /: in) { (buf, ca) ⇒
|
||||||
for ((k, v) ← bindings) {
|
buf.indexWhere(_._1 isAssignableFrom ca._1) match {
|
||||||
serializerMap.put(k, serializers(v))
|
case -1 ⇒ buf append ca
|
||||||
|
case x ⇒ buf insert (x, ca)
|
||||||
|
}
|
||||||
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* serializerMap is a Map whose keys is the class that is serializable and values is the serializer
|
||||||
|
* to be used for that class.
|
||||||
|
*/
|
||||||
|
private val serializerMap: ConcurrentHashMap[Class[_], Serializer] = {
|
||||||
|
val serializerMap = new ConcurrentHashMap[Class[_], Serializer]
|
||||||
|
for ((c, s) ← bindings) serializerMap.put(c, s)
|
||||||
serializerMap
|
serializerMap
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps from a Serializer Identity (Int) to a Serializer instance (optimization)
|
* Maps from a Serializer Identity (Int) to a Serializer instance (optimization)
|
||||||
*/
|
*/
|
||||||
lazy val serializerByIdentity: Map[Int, Serializer] =
|
val serializerByIdentity: Map[Int, Serializer] =
|
||||||
Map(NullSerializer.identifier -> NullSerializer) ++ serializers map { case (_, v) ⇒ (v.identifier, v) }
|
Map(NullSerializer.identifier -> NullSerializer) ++ serializers map { case (_, v) ⇒ (v.identifier, v) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,61 +54,7 @@ public class SerializationDocTestBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#my-own-serializer
|
//#my-own-serializer
|
||||||
@Test public void haveExamples() {
|
|
||||||
/*
|
|
||||||
//#serialize-messages-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serialize-messages = on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialize-messages-config
|
|
||||||
|
|
||||||
//#serialize-creators-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serialize-creators = on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialize-creators-config
|
|
||||||
|
|
||||||
|
|
||||||
//#serialize-serializers-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
|
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialize-serializers-config
|
|
||||||
|
|
||||||
//#serialization-bindings-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
java = "akka.serialization.JavaSerializer"
|
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
java = ["java.lang.String",
|
|
||||||
"app.my.Customer"]
|
|
||||||
proto = ["com.google.protobuf.Message"]
|
|
||||||
myown = ["my.own.BusinessObject",
|
|
||||||
"something.equally.Awesome",
|
|
||||||
"akka.docs.serialization.MyOwnSerializable"
|
|
||||||
"java.lang.Boolean"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialization-bindings-config
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void demonstrateTheProgrammaticAPI() {
|
@Test public void demonstrateTheProgrammaticAPI() {
|
||||||
//#programmatic
|
//#programmatic
|
||||||
|
|
|
||||||
|
|
@ -25,47 +25,28 @@ For Akka to know which ``Serializer`` to use for what, you need edit your :ref:`
|
||||||
in the "akka.actor.serializers"-section you bind names to implementations of the ``akka.serialization.Serializer``
|
in the "akka.actor.serializers"-section you bind names to implementations of the ``akka.serialization.Serializer``
|
||||||
you wish to use, like this:
|
you wish to use, like this:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialize-serializers-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialize-serializers-config
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The name ``default`` is special in the sense that the ``Serializer``
|
|
||||||
mapped to it will be used as default.
|
|
||||||
|
|
||||||
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
||||||
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialization-bindings-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
|
||||||
|
|
||||||
.. note::
|
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 if the messages implements
|
Akka provides serializers for ``java.io.Serializable`` and `protobuf <http://code.google.com/p/protobuf/>`_
|
||||||
that. E.g. ``com.google.protobuf.Message`` for protobuf serialization.
|
``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``
|
||||||
Protobuf
|
and ``com.google.protobuf.Message``.
|
||||||
--------
|
|
||||||
|
|
||||||
Akka provides a ``Serializer`` for `protobuf <http://code.google.com/p/protobuf/>`_ messages.
|
|
||||||
To use that you need to add the following to the configuration::
|
|
||||||
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
proto = ["com.google.protobuf.Message"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Verification
|
Verification
|
||||||
------------
|
------------
|
||||||
|
|
||||||
If you want to verify that your messages are serializable you can enable the following config option:
|
If you want to verify that your messages are serializable you can enable the following config option:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialize-messages-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialize-messages-config
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
|
@ -74,7 +55,7 @@ If you want to verify that your messages are serializable you can enable the fol
|
||||||
|
|
||||||
If you want to verify that your ``Props`` are serializable you can enable the following config option:
|
If you want to verify that your ``Props`` are serializable you can enable the following config option:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialize-creators-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialize-creators-config
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ class MyOwnSerializer extends Serializer {
|
||||||
}
|
}
|
||||||
//#my-own-serializer
|
//#my-own-serializer
|
||||||
|
|
||||||
|
trait MyOwnSerializable
|
||||||
|
case class Customer(name: String) extends MyOwnSerializable
|
||||||
|
|
||||||
class SerializationDocSpec extends AkkaSpec {
|
class SerializationDocSpec extends AkkaSpec {
|
||||||
"demonstrate configuration of serialize messages" in {
|
"demonstrate configuration of serialize messages" in {
|
||||||
//#serialize-messages-config
|
//#serialize-messages-config
|
||||||
|
|
@ -82,8 +85,8 @@ class SerializationDocSpec extends AkkaSpec {
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serializers {
|
serializers {
|
||||||
default = "akka.serialization.JavaSerializer"
|
java = "akka.serialization.JavaSerializer"
|
||||||
|
proto = "akka.serialization.ProtobufSerializer"
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
myown = "akka.docs.serialization.MyOwnSerializer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,8 +94,6 @@ class SerializationDocSpec extends AkkaSpec {
|
||||||
""")
|
""")
|
||||||
//#serialize-serializers-config
|
//#serialize-serializers-config
|
||||||
val a = ActorSystem("system", config)
|
val a = ActorSystem("system", config)
|
||||||
SerializationExtension(a).serializers("default").getClass.getName must equal("akka.serialization.JavaSerializer")
|
|
||||||
SerializationExtension(a).serializers("myown").getClass.getName must equal("akka.docs.serialization.MyOwnSerializer")
|
|
||||||
a.shutdown()
|
a.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,31 +103,26 @@ class SerializationDocSpec extends AkkaSpec {
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serializers {
|
serializers {
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
java = "akka.serialization.JavaSerializer"
|
java = "akka.serialization.JavaSerializer"
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
proto = "akka.serialization.ProtobufSerializer"
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
myown = "akka.docs.serialization.MyOwnSerializer"
|
||||||
}
|
}
|
||||||
|
|
||||||
serialization-bindings {
|
serialization-bindings {
|
||||||
java = ["java.lang.String",
|
"java.lang.String" = java
|
||||||
"app.my.Customer"]
|
"akka.docs.serialization.Customer" = java
|
||||||
proto = ["com.google.protobuf.Message"]
|
"com.google.protobuf.Message" = proto
|
||||||
myown = ["my.own.BusinessObject",
|
"akka.docs.serialization.MyOwnSerializable" = myown
|
||||||
"something.equally.Awesome",
|
"java.lang.Boolean" = myown
|
||||||
"akka.docs.serialization.MyOwnSerializable"
|
}
|
||||||
"java.lang.Boolean"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
//#serialization-bindings-config
|
//#serialization-bindings-config
|
||||||
val a = ActorSystem("system", config)
|
val a = ActorSystem("system", config)
|
||||||
SerializationExtension(a).serializers("default").getClass.getName must equal("akka.serialization.JavaSerializer")
|
SerializationExtension(a).serializerFor(classOf[String]).getClass must equal(classOf[JavaSerializer])
|
||||||
SerializationExtension(a).serializers("java").getClass.getName must equal("akka.serialization.JavaSerializer")
|
SerializationExtension(a).serializerFor(classOf[Customer]).getClass must equal(classOf[JavaSerializer])
|
||||||
SerializationExtension(a).serializers("myown").getClass.getName must equal("akka.docs.serialization.MyOwnSerializer")
|
SerializationExtension(a).serializerFor(classOf[java.lang.Boolean]).getClass must equal(classOf[MyOwnSerializer])
|
||||||
SerializationExtension(a).serializerFor(classOf[String]).getClass.getName must equal("akka.serialization.JavaSerializer")
|
|
||||||
SerializationExtension(a).serializerFor(classOf[java.lang.Boolean]).getClass.getName must equal("akka.docs.serialization.MyOwnSerializer")
|
|
||||||
a.shutdown()
|
a.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,38 +27,19 @@ you wish to use, like this:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialize-serializers-config
|
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialize-serializers-config
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The name ``default`` is special in the sense that the ``Serializer``
|
|
||||||
mapped to it will be used as default.
|
|
||||||
|
|
||||||
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
||||||
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
|
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
|
||||||
|
|
||||||
.. note::
|
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 if the messages implements
|
Akka provides serializers for ``java.io.Serializable`` and `protobuf <http://code.google.com/p/protobuf/>`_
|
||||||
that. E.g. ``com.google.protobuf.Message`` for protobuf serialization.
|
``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``
|
||||||
Protobuf
|
and ``com.google.protobuf.Message``.
|
||||||
--------
|
|
||||||
|
|
||||||
Akka provides a ``Serializer`` for `protobuf <http://code.google.com/p/protobuf/>`_ messages.
|
|
||||||
To use that you need to add the following to the configuration::
|
|
||||||
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
proto = ["com.google.protobuf.Message"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Verification
|
Verification
|
||||||
------------
|
------------
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,21 @@ akka {
|
||||||
|
|
||||||
actor {
|
actor {
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
serialization-bindings {
|
||||||
|
"com.google.protobuf.Message" = proto
|
||||||
|
"java.io.Serializable" = java
|
||||||
|
}
|
||||||
|
|
||||||
deployment {
|
deployment {
|
||||||
|
|
||||||
default {
|
default {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.serialization
|
||||||
|
|
||||||
|
import akka.testkit.AkkaSpec
|
||||||
|
import akka.remote.RemoteProtocol.MessageProtocol
|
||||||
|
import akka.actor.ProtobufProtocol.MyMessage
|
||||||
|
|
||||||
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
class ProtobufSerializerSpec extends AkkaSpec {
|
||||||
|
|
||||||
|
val ser = SerializationExtension(system)
|
||||||
|
|
||||||
|
"Serialization" must {
|
||||||
|
|
||||||
|
"resolve protobuf serializer" in {
|
||||||
|
ser.serializerFor(classOf[MessageProtocol]).getClass must be(classOf[ProtobufSerializer])
|
||||||
|
ser.serializerFor(classOf[MyMessage]).getClass must be(classOf[ProtobufSerializer])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue