Adding Java docs for Serialization, and discovered some flaws with the Java API, that have been fixed
This commit is contained in:
parent
d8b2f88ced
commit
fbb7cb20a1
10 changed files with 266 additions and 57 deletions
|
|
@ -17,7 +17,7 @@ import com.google.protobuf.Message
|
|||
class ProtobufSerializer extends Serializer {
|
||||
val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]])
|
||||
def includeManifest: Boolean = true
|
||||
def identifier = 2: Serializer.Identifier
|
||||
def identifier = 2
|
||||
|
||||
def toBinary(obj: AnyRef): Array[Byte] = {
|
||||
if (!obj.isInstanceOf[Message]) throw new IllegalArgumentException(
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
|
|||
|
||||
private def writeReplace(): AnyRef = parameters match {
|
||||
case null ⇒ SerializedMethodCall(method.getDeclaringClass, method.getName, method.getParameterTypes, null, null)
|
||||
case ps if ps.length == 0 ⇒ SerializedMethodCall(method.getDeclaringClass, method.getName, method.getParameterTypes, Array[Serializer.Identifier](), Array[Array[Byte]]())
|
||||
case ps if ps.length == 0 ⇒ SerializedMethodCall(method.getDeclaringClass, method.getName, method.getParameterTypes, Array[Int](), Array[Array[Byte]]())
|
||||
case ps ⇒
|
||||
val serializers: Array[Serializer] = ps map SerializationExtension(Serialization.currentSystem.value).findSerializerFor
|
||||
val serializedParameters: Array[Array[Byte]] = Array.ofDim[Array[Byte]](serializers.length)
|
||||
|
|
@ -207,7 +207,7 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
|
|||
/**
|
||||
* Represents the serialized form of a MethodCall, uses readResolve and writeReplace to marshall the call
|
||||
*/
|
||||
case class SerializedMethodCall(ownerType: Class[_], methodName: String, parameterTypes: Array[Class[_]], serializerIdentifiers: Array[Serializer.Identifier], serializedParameters: Array[Array[Byte]]) {
|
||||
case class SerializedMethodCall(ownerType: Class[_], methodName: String, parameterTypes: Array[Class[_]], serializerIdentifiers: Array[Int], serializedParameters: Array[Array[Byte]]) {
|
||||
|
||||
//TODO implement writeObject and readObject to serialize
|
||||
//TODO Possible optimization is to special encode the parameter-types to conserve space
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class Serialization(val system: ActorSystemImpl) extends Extension {
|
|||
* Returns either the resulting object or an Exception if one was thrown.
|
||||
*/
|
||||
def deserialize(bytes: Array[Byte],
|
||||
serializerId: Serializer.Identifier,
|
||||
serializerId: Int,
|
||||
clazz: Option[Class[_]],
|
||||
classLoader: Option[ClassLoader]): Either[Exception, AnyRef] =
|
||||
try {
|
||||
|
|
@ -164,9 +164,9 @@ class Serialization(val system: ActorSystemImpl) extends Extension {
|
|||
lazy val serializerMap: Map[String, Serializer] = bindings mapValues serializers
|
||||
|
||||
/**
|
||||
* Maps from a Serializer.Identifier (Byte) to a Serializer instance (optimization)
|
||||
* Maps from a Serializer Identity (Int) to a Serializer instance (optimization)
|
||||
*/
|
||||
lazy val serializerByIdentity: Map[Serializer.Identifier, Serializer] =
|
||||
lazy val serializerByIdentity: Map[Int, Serializer] =
|
||||
Map(NullSerializer.identifier -> NullSerializer) ++ serializers map { case (_, v) ⇒ (v.identifier, v) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ package akka.serialization
|
|||
import java.io.{ ObjectOutputStream, ByteArrayOutputStream, ObjectInputStream, ByteArrayInputStream }
|
||||
import akka.util.ClassLoaderObjectInputStream
|
||||
|
||||
object Serializer {
|
||||
type Identifier = Int
|
||||
}
|
||||
|
||||
/**
|
||||
* A Serializer represents a bimap between an object and an array of bytes representing that object
|
||||
*/
|
||||
|
|
@ -19,7 +15,7 @@ trait Serializer extends scala.Serializable {
|
|||
* Completely unique value to identify this implementation of Serializer, used to optimize network traffic
|
||||
* Values from 0 to 16 is reserved for Akka internal usage
|
||||
*/
|
||||
def identifier: Serializer.Identifier
|
||||
def identifier: Int
|
||||
|
||||
/**
|
||||
* Serializes the given object into an Array of Byte
|
||||
|
|
@ -31,10 +27,34 @@ trait Serializer extends scala.Serializable {
|
|||
*/
|
||||
def includeManifest: Boolean
|
||||
|
||||
/**
|
||||
* Deserializes the given Array of Bytes into an AnyRef
|
||||
*/
|
||||
def fromBinary(bytes: Array[Byte]): AnyRef = fromBinary(bytes, None, None)
|
||||
|
||||
/**
|
||||
* Deserializes the given Array of Bytes into an AnyRef with an optional type hint
|
||||
*/
|
||||
def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]]): AnyRef = fromBinary(bytes, manifest, None)
|
||||
|
||||
/**
|
||||
* Produces an object from an array of bytes, with an optional type-hint and a classloader to load the class into
|
||||
*/
|
||||
def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]] = None, classLoader: Option[ClassLoader] = None): AnyRef
|
||||
def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]], classLoader: Option[ClassLoader]): AnyRef
|
||||
}
|
||||
|
||||
/**
|
||||
* Java API for creating a Serializer
|
||||
*/
|
||||
abstract class JSerializer extends Serializer {
|
||||
def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]] = None, classLoader: Option[ClassLoader] = None): AnyRef =
|
||||
fromBinary(bytes, manifest.orNull, classLoader.orNull)
|
||||
|
||||
/**
|
||||
* This method should be overridden,
|
||||
* manifest and classLoader may be null.
|
||||
*/
|
||||
def fromBinary(bytes: Array[Byte], manifest: Class[_], classLoader: ClassLoader): AnyRef
|
||||
}
|
||||
|
||||
object JavaSerializer extends JavaSerializer
|
||||
|
|
@ -47,7 +67,7 @@ class JavaSerializer extends Serializer {
|
|||
|
||||
def includeManifest: Boolean = false
|
||||
|
||||
def identifier = 1: Serializer.Identifier
|
||||
def identifier = 1
|
||||
|
||||
def toBinary(o: AnyRef): Array[Byte] = {
|
||||
val bos = new ByteArrayOutputStream
|
||||
|
|
@ -74,7 +94,7 @@ class JavaSerializer extends Serializer {
|
|||
class NullSerializer extends Serializer {
|
||||
val nullAsBytes = Array[Byte]()
|
||||
def includeManifest: Boolean = false
|
||||
def identifier = 0: Serializer.Identifier
|
||||
def identifier = 0
|
||||
def toBinary(o: AnyRef) = nullAsBytes
|
||||
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]] = None, classLoader: Option[ClassLoader] = None): AnyRef = null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.serialization
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class SerializationDocTest extends SerializationDocTestBase with JUnitSuite
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.serialization;
|
||||
|
||||
import akka.serialization.JSerializer;
|
||||
import akka.serialization.Serialization;
|
||||
import akka.serialization.SerializationExtension;
|
||||
import akka.serialization.Serializer;
|
||||
import org.junit.Test;
|
||||
//#imports
|
||||
|
||||
import akka.serialization.*;
|
||||
import akka.actor.ActorSystem;
|
||||
import com.typesafe.config.*;
|
||||
|
||||
//#imports
|
||||
|
||||
public class SerializationDocTestBase {
|
||||
//#my-own-serializer
|
||||
public static class MyOwnSerializer extends JSerializer {
|
||||
|
||||
// This is whether "fromBinary" requires a "clazz" or not
|
||||
@Override public boolean includeManifest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pick a unique identifier for your Serializer,
|
||||
// you've got a couple of billions to choose from,
|
||||
// 0 - 16 is reserved by Akka itself
|
||||
@Override public int identifier() {
|
||||
return 1234567;
|
||||
}
|
||||
|
||||
// "toBinary" serializes the given object to an Array of Bytes
|
||||
@Override public byte[] toBinary(Object obj) {
|
||||
// Put the code that serializes the object here
|
||||
//#...
|
||||
return new byte[0];
|
||||
//#...
|
||||
}
|
||||
|
||||
// "fromBinary" deserializes the given array,
|
||||
// using the type hint (if any, see "includeManifest" above)
|
||||
// into the optionally provided classLoader.
|
||||
@Override public Object fromBinary(byte[] bytes,
|
||||
Class clazz,
|
||||
ClassLoader classLoader) {
|
||||
// Put your code that deserializes here
|
||||
//#...
|
||||
return null;
|
||||
//#...
|
||||
}
|
||||
}
|
||||
//#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"
|
||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
||||
}
|
||||
|
||||
serialization-bindings {
|
||||
java = ["java.lang.String",
|
||||
"app.my.Customer"]
|
||||
myown = ["my.own.BusinessObject",
|
||||
"something.equally.Awesome",
|
||||
"java.lang.Boolean"]
|
||||
}
|
||||
}
|
||||
}
|
||||
//#serialization-bindings-config
|
||||
*/
|
||||
}
|
||||
|
||||
@Test public void demonstrateTheProgrammaticAPI() {
|
||||
//#programmatic
|
||||
ActorSystem system = ActorSystem.create("example");
|
||||
|
||||
// Get the Serialization Extension
|
||||
Serialization serialization = SerializationExtension.get(system);
|
||||
|
||||
// Have something to serialize
|
||||
String original = "woohoo";
|
||||
|
||||
// Find the Serializer for it
|
||||
Serializer serializer = serialization.findSerializerFor(original);
|
||||
|
||||
// Turn it into bytes
|
||||
byte[] bytes = serializer.toBinary(original);
|
||||
|
||||
// Turn it back into an object,
|
||||
// the nulls are for the class manifest and for the classloader
|
||||
String back = (String)serializer.fromBinary(bytes);
|
||||
|
||||
// Voilá!
|
||||
assertEquals(original, back);
|
||||
|
||||
//#programmatic
|
||||
system.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,90 @@
|
|||
Serialization (Java)
|
||||
#####################
|
||||
|
||||
Serialization will soon be documented.
|
||||
.. sidebar:: Contents
|
||||
|
||||
Until then we refer to the following section in the configuration file:
|
||||
.. contents:: :local:
|
||||
|
||||
* `Serializers <https://github.com/jboner/akka/blob/master/akka-actor/src/main/resources/reference.conf#L180>`_
|
||||
Akka has a built-in Extension for serialization,
|
||||
and it is both possible to use the built-in serializers and to write your own.
|
||||
|
||||
The serialization mechanism is both used by Akka internally to serialize messages,
|
||||
and available for ad-hoc serialization of whatever you might need it for.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
For Akka to know which ``Serializer`` to use for what, you need edit your Akka Configuration,
|
||||
in the "akka.actor.serializers"-section you bind names to implementations of the ``akka.serialization.Serializer``
|
||||
you wish to use, like this:
|
||||
|
||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#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
|
||||
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
|
||||
|
||||
.. note::
|
||||
|
||||
Akka currently only checks for absolute equality of Classes, i.e. it does not yet check ``isAssignableFrom``,
|
||||
this means that you'll need to list the specific classes.
|
||||
|
||||
Verification
|
||||
------------
|
||||
|
||||
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
|
||||
|
||||
.. warning::
|
||||
|
||||
We only recommend using the config option turned on when you're running tests.
|
||||
It is completely pointless to have it turned on in other scenarios.
|
||||
|
||||
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
|
||||
|
||||
.. warning::
|
||||
|
||||
We only recommend using the config option turned on when you're running tests.
|
||||
It is completely pointless to have it turned on in other scenarios.
|
||||
|
||||
Programmatic
|
||||
------------
|
||||
|
||||
If you want to programmatically serialize/deserialize using Akka Serialization,
|
||||
here's some examples:
|
||||
|
||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||
:include: imports,programmatic
|
||||
|
||||
For more information, have a look at the ``ScalaDoc`` for ``akka.serialization._``
|
||||
|
||||
Customization
|
||||
=============
|
||||
|
||||
So, lets say that you want to create your own ``Serializer``,
|
||||
you saw the ``akka.docs.serialization.MyOwnSerializer`` in the config example above?
|
||||
|
||||
Creating new Serializers
|
||||
------------------------
|
||||
|
||||
First you need to create a class definition of your ``Serializer``,
|
||||
which is done by extending ``akka.serialization.JSerializer``, like this:
|
||||
|
||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||
:include: imports,my-own-serializer
|
||||
:exclude: ...
|
||||
|
||||
Then you only need to fill in the blanks, bind it to a name in your Akka Configuration and then
|
||||
list which classes that should be serialized using it.
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.extension
|
||||
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
class SerializationDocSpec extends WordSpec with MustMatchers {
|
||||
|
||||
"demonstrate how to use Serialization" in {
|
||||
"""
|
||||
serializers {
|
||||
# java = "akka.serialization.JavaSerializer"
|
||||
# proto = "akka.serialization.ProtobufSerializer"
|
||||
|
||||
default = "akka.serialization.JavaSerializer"
|
||||
}
|
||||
|
||||
# serialization-bindings {
|
||||
# java = ["akka.serialization.SerializeSpec$Address",
|
||||
# "akka.serialization.MyJavaSerializableActor",
|
||||
# "akka.serialization.MyStatelessActorWithMessagesInMailbox",
|
||||
# "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
||||
# proto = ["com.google.protobuf.Message",
|
||||
# "akka.actor.ProtobufProtocol$MyMessage"]
|
||||
# }
|
||||
"""
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,17 +5,12 @@ package akka.docs.serialization
|
|||
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import akka.testkit._
|
||||
//#imports
|
||||
import akka.actor.ActorSystem
|
||||
import akka.serialization._
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
//#imports
|
||||
import akka.serialization._
|
||||
|
||||
//#imports
|
||||
|
||||
object SerializationDocSpec {
|
||||
|
||||
}
|
||||
|
||||
//#my-own-serializer
|
||||
class MyOwnSerializer extends Serializer {
|
||||
|
|
@ -26,7 +21,7 @@ class MyOwnSerializer extends Serializer {
|
|||
// Pick a unique identifier for your Serializer,
|
||||
// you've got a couple of billions to choose from,
|
||||
// 0 - 16 is reserved by Akka itself
|
||||
def identifier = 1234567: Serializer.Identifier
|
||||
def identifier = 1234567
|
||||
|
||||
// "toBinary" serializes the given object to an Array of Bytes
|
||||
def toBinary(obj: AnyRef): Array[Byte] = {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.google.protobuf.Message
|
|||
class ProtobufSerializer extends Serializer {
|
||||
val ARRAY_OF_BYTE_ARRAY = Array[Class[_]](classOf[Array[Byte]])
|
||||
def includeManifest: Boolean = true
|
||||
def identifier = 2: Serializer.Identifier
|
||||
def identifier = 2
|
||||
|
||||
def toBinary(obj: AnyRef): Array[Byte] = obj match {
|
||||
case m: Message ⇒ m.toByteArray
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue