+act #17576 Support serializer with string manifest

* useful when evolution is needed, e.g. Akka Persistence

* docs, comments, cluster-metrics and cluster-tools serializers
This commit is contained in:
Patrik Nordwall 2015-05-28 18:42:22 +02:00
parent aeb2302c2f
commit 740f006a38
21 changed files with 605 additions and 93 deletions

View file

@ -26,7 +26,7 @@ import akka.serialization.JavaSerializer.CurrentSystem
* load classes using reflection.</li>
* </ul>
*
* <b>Be sure to always use the PropertyManager for loading classes!</b> This is necessary to
* <b>Be sure to always use the [[akka.actor.DynamicAccess]] for loading classes!</b> This is necessary to
* avoid strange match errors and inequalities which arise from different class loaders loading
* the same class.
*/
@ -65,6 +65,74 @@ trait Serializer {
final def fromBinary(bytes: Array[Byte], clazz: Class[_]): AnyRef = fromBinary(bytes, Option(clazz))
}
/**
* A Serializer represents a bimap between an object and an array of bytes representing that object.
*
* For serialization of data that need to evolve over time the `SerializerWithStringManifest` is recommended instead
* of [[Serializer]] because the manifest (type hint) is a `String` instead of a `Class`. That means
* that the class can be moved/removed and the serializer can still deserialize old data by matching
* on the `String`. This is especially useful for Akka Persistence.
*
* The manifest string can also encode a version number that can be used in [[#fromBinary]] to
* deserialize in different ways to migrate old data to new domain objects.
*
* If the data was originally serialized with [[Serializer]] and in a later version of the
* system you change to `SerializerWithStringManifest` the manifest string will be the full class name if
* you used `includeManifest=true`, otherwise it will be the empty string.
*
* Serializers are loaded using reflection during [[akka.actor.ActorSystem]]
* start-up, where two constructors are tried in order:
*
* <ul>
* <li>taking exactly one argument of type [[akka.actor.ExtendedActorSystem]];
* this should be the preferred one because all reflective loading of classes
* during deserialization should use ExtendedActorSystem.dynamicAccess (see
* [[akka.actor.DynamicAccess]]), and</li>
* <li>without arguments, which is only an option if the serializer does not
* load classes using reflection.</li>
* </ul>
*
* <b>Be sure to always use the [[akka.actor.DynamicAccess]] for loading classes!</b> This is necessary to
* avoid strange match errors and inequalities which arise from different class loaders loading
* the same class.
*/
abstract class SerializerWithStringManifest extends Serializer {
/**
* Completely unique value to identify this implementation of Serializer, used to optimize network traffic.
* Values from 0 to 16 are reserved for Akka internal usage.
*/
def identifier: Int
final override def includeManifest: Boolean = true
/**
* Return the manifest (type hint) that will be provided in the fromBinary method.
* Use `""` if manifest is not needed.
*/
def manifest(o: AnyRef): String
/**
* Serializes the given object into an Array of Byte
*/
def toBinary(o: AnyRef): Array[Byte]
/**
* Produces an object from an array of bytes, with an optional type-hint;
* the class should be loaded using ActorSystem.dynamicAccess.
*/
def fromBinary(bytes: Array[Byte], manifest: String): AnyRef
final def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]]): AnyRef = {
val manifestString = manifest match {
case Some(c) c.getName
case None ""
}
fromBinary(bytes, manifestString)
}
}
/**
* Base serializer trait with serialization identifiers configuration contract,
* when globally unique serialization identifier is configured in the `reference.conf`.