Reviewed and improved serialization docs, still error with in/out, waiting for answer from Debasish

This commit is contained in:
Patrik Nordwall 2011-05-02 12:45:54 +02:00
parent 3b3f8d307a
commit 2a26a707ef
2 changed files with 87 additions and 119 deletions

View file

@ -9,28 +9,6 @@ Serialization (Java)
Akka serialization module has been documented extensively under the :ref:`serialization-scala` section. In this section we will point out the different APIs that are available in Akka for Java based serialization of ActorRefs. The Scala APIs of ActorSerialization has implicit Format objects that set up the type class based serialization. In the Java API, the Format objects need to be specified explicitly. Akka serialization module has been documented extensively under the :ref:`serialization-scala` section. In this section we will point out the different APIs that are available in Akka for Java based serialization of ActorRefs. The Scala APIs of ActorSerialization has implicit Format objects that set up the type class based serialization. In the Java API, the Format objects need to be specified explicitly.
Serialization of ActorRef
-------------------------
The following are the Java APIs for serialization of local ActorRefs:
.. code-block:: scala
/**
* Module for local actor serialization.
*/
object ActorSerialization {
// wrapper for implicits to be used by Java
def fromBinaryJ[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef =
fromBinary(bytes)(format)
// wrapper for implicits to be used by Java
def toBinaryJ[T <: Actor](a: ActorRef, format: Format[T], srlMailBox: Boolean = true): Array[Byte] =
toBinary(a, srlMailBox)(format)
}
The following steps describe the procedure for serializing an Actor and ActorRef.
Serialization of a Stateless Actor Serialization of a Stateless Actor
---------------------------------- ----------------------------------
@ -38,6 +16,8 @@ Step 1: Define the Actor
.. code-block:: scala .. code-block:: scala
import akka.actor.UntypedActor;
public class SerializationTestActor extends UntypedActor { public class SerializationTestActor extends UntypedActor {
public void onReceive(Object msg) { public void onReceive(Object msg) {
getContext().replySafe("got it!"); getContext().replySafe("got it!");
@ -50,6 +30,8 @@ Note how the generated Java classes are accessed using the $class based naming c
.. code-block:: scala .. code-block:: scala
import akka.serialization.StatelessActorFormat;
class SerializationTestActorFormat implements StatelessActorFormat<SerializationTestActor> { class SerializationTestActorFormat implements StatelessActorFormat<SerializationTestActor> {
@Override @Override
public SerializationTestActor fromBinary(byte[] bytes, SerializationTestActor act) { public SerializationTestActor fromBinary(byte[] bytes, SerializationTestActor act) {
@ -68,6 +50,14 @@ The following JUnit snippet first creates an actor using the default constructor
.. code-block:: java .. code-block:: java
import akka.actor.ActorRef;
import akka.actor.ActorTimeoutException;
import akka.actor.Actors;
import akka.actor.UntypedActor;
import akka.serialization.Format;
import akka.serialization.StatelessActorFormat;
import static akka.serialization.ActorSerialization.*;
@Test public void mustBeAbleToSerializeAfterCreateActorRefFromClass() { @Test public void mustBeAbleToSerializeAfterCreateActorRefFromClass() {
ActorRef ref = Actors.actorOf(SerializationTestActor.class); ActorRef ref = Actors.actorOf(SerializationTestActor.class);
assertNotNull(ref); assertNotNull(ref);
@ -101,21 +91,23 @@ Let's now have a look at how to serialize an actor that carries a state with it.
Step 1: Define the Actor Step 1: Define the Actor
Here we consider an actor defined in Scala. We will however serialize using the Java APIs.
.. code-block:: scala .. code-block:: scala
class MyUntypedActor extends UntypedActor { import akka.actor.UntypedActor;
var count = 0
def onReceive(message: Any): Unit = message match { public class MyUntypedActor extends UntypedActor {
case m: String if m == "hello" => int count = 0;
count = count + 1
getContext.replyUnsafe("world " + count) public void onReceive(Object msg) {
case m: String => if (msg.equals("hello")) {
count = count + 1 count = count + 1;
getContext.replyUnsafe("hello " + m + " " + count) getContext().replyUnsafe("world " + count);
case _ => } else if (msg instanceof String) {
throw new Exception("invalid message type") count = count + 1;
getContext().replyUnsafe("hello " + msg + " " + count);
} else {
throw new IllegalArgumentException("invalid message type");
}
} }
} }
@ -125,27 +117,37 @@ Step 2: Define the instance of the typeclass
.. code-block:: java .. code-block:: java
class MyUntypedActorFormat implements Format<MyUntypedActor> { import akka.actor.UntypedActor;
@Override import akka.serialization.Format;
public MyUntypedActor fromBinary(byte[] bytes, MyUntypedActor act) { import akka.serialization.SerializerFactory;
ProtobufProtocol.Counter p =
(ProtobufProtocol.Counter) new SerializerFactory().getProtobuf().fromBinary(bytes, ProtobufProtocol.Counter.class);
act.count_$eq(p.getCount());
return act;
}
@Override class MyUntypedActorFormat implements Format<MyUntypedActor> {
public byte[] toBinary(MyUntypedActor ac) { @Override
return ProtobufProtocol.Counter.newBuilder().setCount(ac.count()).build().toByteArray(); public MyUntypedActor fromBinary(byte[] bytes, MyUntypedActor act) {
} ProtobufProtocol.Counter p =
(ProtobufProtocol.Counter) new SerializerFactory().getProtobuf().fromBinary(bytes, ProtobufProtocol.Counter.class);
act.count = p.getCount();
return act;
} }
Note the usage of Protocol Buffers to serialize the state of the actor. @Override
public byte[] toBinary(MyUntypedActor ac) {
return ProtobufProtocol.Counter.newBuilder().setCount(ac.count()).build().toByteArray();
}
}
Note the usage of Protocol Buffers to serialize the state of the actor. ProtobufProtocol.Counter is something
you need to define yourself
Step 3: Serialize and de-serialize Step 3: Serialize and de-serialize
.. code-block:: java .. code-block:: java
import akka.actor.ActorRef;
import akka.actor.ActorTimeoutException;
import akka.actor.Actors;
import static akka.serialization.ActorSerialization.*;
@Test public void mustBeAbleToSerializeAStatefulActor() { @Test public void mustBeAbleToSerializeAStatefulActor() {
ActorRef ref = Actors.actorOf(MyUntypedActor.class); ActorRef ref = Actors.actorOf(MyUntypedActor.class);
assertNotNull(ref); assertNotNull(ref);

View file

@ -58,10 +58,15 @@ Step 1: Define the actor
} }
} }
Step 2: Implement the type class for the actor Step 2: Implement the type class for the actor. ProtobufProtocol.Counter is something you need to define yourself, as
explained in the Protobuf section.
.. code-block:: scala .. code-block:: scala
import akka.serialization.{Serializer, Format}
import akka.actor.Actor
import akka.actor.Actor._
object BinaryFormatMyActor { object BinaryFormatMyActor {
implicit object MyActorFormat extends Format[MyActor] { implicit object MyActorFormat extends Format[MyActor] {
def fromBinary(bytes: Array[Byte], act: MyActor) = { def fromBinary(bytes: Array[Byte], act: MyActor) = {
@ -70,8 +75,7 @@ Step 2: Implement the type class for the actor
act act
} }
def toBinary(ac: MyActor) = def toBinary(ac: MyActor) =
ProtobufProtocol.Counter.newBuilder.setCount(ac.count).build.toByteArray ProtobufProtocol.Counter.newBuilder.setCount(ac.count).build.toByteArray
}
} }
} }
@ -159,7 +163,7 @@ For a Java serializable actor:
.. code-block:: scala .. code-block:: scala
@serializable class MyJavaSerializableActor extends Actor { class MyJavaSerializableActor extends Actor with scala.Serializable {
var count = 0 var count = 0
def receive = { def receive = {
@ -173,6 +177,8 @@ Create a module for the type class ..
.. code-block:: scala .. code-block:: scala
import akka.serialization.{SerializerBasedActorFormat, Serializer}
object BinaryFormatMyJavaSerializableActor { object BinaryFormatMyJavaSerializableActor {
implicit object MyJavaSerializableActorFormat extends SerializerBasedActorFormat[MyJavaSerializableActor] { implicit object MyJavaSerializableActorFormat extends SerializerBasedActorFormat[MyJavaSerializableActor] {
val serializer = Serializer.Java val serializer = Serializer.Java
@ -184,6 +190,7 @@ and serialize / de-serialize ..
.. code-block:: scala .. code-block:: scala
it("should be able to serialize and de-serialize a stateful actor with a given serializer") { it("should be able to serialize and de-serialize a stateful actor with a given serializer") {
import akka.actor.Actor._
import akka.serialization.ActorSerialization._ import akka.serialization.ActorSerialization._
import BinaryFormatMyJavaSerializableActor._ import BinaryFormatMyJavaSerializableActor._
@ -202,7 +209,7 @@ Serialization of a RemoteActorRef
You can serialize an ``ActorRef`` to an immutable, network-aware Actor reference that can be freely shared across the network, a reference that "remembers" and stay mapped to its original Actor instance and host node, and will always work as expected. You can serialize an ``ActorRef`` to an immutable, network-aware Actor reference that can be freely shared across the network, a reference that "remembers" and stay mapped to its original Actor instance and host node, and will always work as expected.
The ``RemoteActorRef`` serialization is based upon Protobuf (Google Protocol Buffers) and you don't need to do anything to use it, it works on any ``ActorRef`` (as long as the actor has **not** implemented one of the ``SerializableActor`` traits, since then deep serialization will happen). The ``RemoteActorRef`` serialization is based upon Protobuf (Google Protocol Buffers) and you don't need to do anything to use it, it works on any ``ActorRef``.
Currently Akka will **not** autodetect an ``ActorRef`` as part of your message and serialize it for you automatically, so you have to do that manually or as part of your custom serialization mechanisms. Currently Akka will **not** autodetect an ``ActorRef`` as part of your message and serialize it for you automatically, so you have to do that manually or as part of your custom serialization mechanisms.
@ -218,14 +225,14 @@ To deserialize the ``ActorRef`` to a ``RemoteActorRef`` you need to use the ``fr
.. code-block:: scala .. code-block:: scala
import RemoteActorSerialization._ import akka.serialization.RemoteActorSerialization._
val actor2 = fromBinaryToRemoteActorRef(bytes) val actor2 = fromBinaryToRemoteActorRef(bytes)
You can also pass in a class loader to load the ``ActorRef`` class and dependencies from: You can also pass in a class loader to load the ``ActorRef`` class and dependencies from:
.. code-block:: scala .. code-block:: scala
import RemoteActorSerialization._ import akka.serialization.RemoteActorSerialization._
val actor2 = fromBinaryToRemoteActorRef(bytes, classLoader) val actor2 = fromBinaryToRemoteActorRef(bytes, classLoader)
Deep serialization of a TypedActor Deep serialization of a TypedActor
@ -240,6 +247,8 @@ Step 1: Define the actor
.. code-block:: scala .. code-block:: scala
import akka.actor.TypedActor
trait MyTypedActor { trait MyTypedActor {
def requestReply(s: String) : String def requestReply(s: String) : String
def oneWay() : Unit def oneWay() : Unit
@ -252,12 +261,18 @@ Step 1: Define the actor
count = count + 1 count = count + 1
"world " + count "world " + count
} }
override def oneWay() {
count = count + 1
}
} }
Step 2: Implement the type class for the actor Step 2: Implement the type class for the actor
.. code-block:: scala .. code-block:: scala
import akka.serialization.{Serializer, Format}
class MyTypedActorFormat extends Format[MyTypedActorImpl] { class MyTypedActorFormat extends Format[MyTypedActorImpl] {
def fromBinary(bytes: Array[Byte], act: MyTypedActorImpl) = { def fromBinary(bytes: Array[Byte], act: MyTypedActorImpl) = {
val p = Serializer.Protobuf.fromBinary(bytes, Some(classOf[ProtobufProtocol.Counter])).asInstanceOf[ProtobufProtocol.Counter] val p = Serializer.Protobuf.fromBinary(bytes, Some(classOf[ProtobufProtocol.Counter])).asInstanceOf[ProtobufProtocol.Counter]
@ -271,6 +286,8 @@ Step 3: Import the type class module definition and serialize / de-serialize
.. code-block:: scala .. code-block:: scala
import akka.serialization.TypedActorSerialization._
val typedActor1 = TypedActor.newInstance(classOf[MyTypedActor], classOf[MyTypedActorImpl], 1000) val typedActor1 = TypedActor.newInstance(classOf[MyTypedActor], classOf[MyTypedActorImpl], 1000)
val f = new MyTypedActorFormat val f = new MyTypedActorFormat
@ -288,7 +305,7 @@ To deserialize the TypedActor to a ``RemoteTypedActorRef`` (an aspectwerkz proxy
.. code-block:: scala .. code-block:: scala
import RemoteTypedActorSerialization._ import akka.serialization.RemoteTypedActorSerialization._
val typedActor = fromBinaryToRemoteTypedActorRef(bytes) val typedActor = fromBinaryToRemoteTypedActorRef(bytes)
// you can also pass in a class loader // you can also pass in a class loader
@ -328,7 +345,6 @@ The ones currently supported are (besides the default which is regular Java seri
- ScalaJSON (Scala only) - ScalaJSON (Scala only)
- JavaJSON (Java but some Scala structures) - JavaJSON (Java but some Scala structures)
- SBinary (Scala only)
- Protobuf (Scala and Java) - Protobuf (Scala and Java)
Apart from the above, Akka also supports Scala object serialization through `SJSON <http://github.com/debasishg/sjson/tree/master>`_ that implements APIs similar to ``akka.serialization.Serializer.*``. See the section on SJSON below for details. Apart from the above, Akka also supports Scala object serialization through `SJSON <http://github.com/debasishg/sjson/tree/master>`_ that implements APIs similar to ``akka.serialization.Serializer.*``. See the section on SJSON below for details.
@ -377,15 +393,16 @@ The remote Actor can then receive the Protobuf message typed as-is:
JSON: Scala JSON: Scala
----------- -----------
Use the akka.serialization.Serialization.ScalaJSON base class with its toJSON method. Akkas Scala JSON is based upon the SJSON library. Use the ``akka.serialization.Serializable.ScalaJSON`` base class with its toJSON method. Akkas Scala JSON is based upon the SJSON library.
For your POJOs to be able to serialize themselves you have to extend the ScalaJSON[] trait as follows. JSON serialization is based on a type class protocol which you need to define for your own abstraction. The instance of the type class is defined as an implicit object which is used for serialization and de-serialization. You also need to implement the methods in terms of the APIs which sjson publishes. For your POJOs to be able to serialize themselves you have to extend the ScalaJSON[] trait as follows. JSON serialization is based on a type class protocol which you need to define for your own abstraction. The instance of the type class is defined as an implicit object which is used for serialization and de-serialization. You also need to implement the methods in terms of the APIs which sjson publishes.
.. code-block:: scala .. code-block:: scala
import akka.serialization.Serializer import akka.serialization._
import akka.serialization.Serializable.ScalaJSON import akka.serialization.Serializable.ScalaJSON
import scala.reflect.BeanInfo import akka.serialization.JsonSerialization._
import akka.serialization.DefaultProtocol._
case class MyMessage(val id: String, val value: Tuple2[String, Int]) extends ScalaJSON[MyMessage] { case class MyMessage(val id: String, val value: Tuple2[String, Int]) extends ScalaJSON[MyMessage] {
// type class instance // type class instance
@ -427,7 +444,7 @@ Here are the steps that you need to follow:
.. code-block:: scala .. code-block:: scala
import DefaultProtocol._ import akka.serialization.DefaultProtocol._
implicit val MyMessageFormat: sjson.json.Format[MyMessage] = implicit val MyMessageFormat: sjson.json.Format[MyMessage] =
asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get) asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get)
@ -436,6 +453,7 @@ Here are the steps that you need to follow:
.. code-block:: scala .. code-block:: scala
import akka.serialization.Serializer.ScalaJSON import akka.serialization.Serializer.ScalaJSON
import akka.serialization.JsonSerialization._
val o = MyMessage("dg", ("akka", 100)) val o = MyMessage("dg", ("akka", 100))
fromjson[MyMessage](tojson(o)) should equal(o) fromjson[MyMessage](tojson(o)) should equal(o)
@ -480,7 +498,7 @@ So if you see something like that:
it means, that you haven't got a @BeanInfo annotation on your class. it means, that you haven't got a @BeanInfo annotation on your class.
You may also see this exception when trying to serialize a case class with out an attribute like this: You may also see this exception when trying to serialize a case class without any attributes, like this:
.. code-block:: scala .. code-block:: scala
@ -900,12 +918,15 @@ There are other nifty ways to implement case class serialization using sjson. Fo
JSON: Java JSON: Java
---------- ----------
Use the akka.serialization.Serialization.JavaJSON base class with its toJSONmethod. Akkas Java JSON is based upon the Jackson library. Use the ``akka.serialization.Serializable.JavaJSON`` base class with its toJSONmethod. Akkas Java JSON is based upon the Jackson library.
For your POJOs to be able to serialize themselves you have to extend the JavaJSON trait. For your POJOs to be able to serialize themselves you have to extend the JavaJSON base class.
.. code-block:: java .. code-block:: java
import akka.serialization.Serializable.JavaJSON;
import akka.serialization.SerializerFactory;
class MyMessage extends JavaJSON { class MyMessage extends JavaJSON {
private String name = null; private String name = null;
public MyMessage(String name) { public MyMessage(String name) {
@ -931,59 +952,4 @@ Use the akka.serialization.SerializerFactory.getJavaJSON to do generic JSON seri
Foo fooCopy = factory.getJavaJSON().in(json, Foo.class); Foo fooCopy = factory.getJavaJSON().in(json, Foo.class);
SBinary: Scala
--------------
To serialize Scala structures you can use SBinary serializer. SBinary can serialize all primitives and most default Scala datastructures; such as List, Tuple, Map, Set, BigInt etc.
Here is an example of using the akka.serialization.Serializer.SBinary serializer to serialize standard Scala library objects.
.. code-block:: scala
import akka.serialization.Serializer
import sbinary.DefaultProtocol._ // you always need to import these implicits
val users = List(("user1", "passwd1"), ("user2", "passwd2"), ("user3", "passwd3"))
val bytes = Serializer.SBinary.out(users)
val usersCopy = Serializer.SBinary.in(bytes, Some(classOf[List[Tuple2[String,String]]]))
If you need to serialize your own user-defined objects then you have to do three things:
- Define an empty constructor
- Mix in the Serializable.SBinary[T] trait, and implement its methods:
- fromBytes(bytes: Array[Byte])[T]
- toBytes: Array[Byte]
- Create an implicit sbinary.Format[T] object for your class. Which means that you have to define its two methods:
- reads(in: Input): T; in which you read in all the fields in your object, using read[FieldType](in)and recreate it.
- writes(out: Output, value: T): Unit; in which you write out all the fields in your object, using write[FieldType](out, value.field).
Here is an example:
.. code-block:: scala
case class User(val usernamePassword: Tuple2[String, String], val email: String, val age: Int)
extends Serializable.SBinary[User] {
import sbinary.DefaultProtocol._
import sbinary.Operations._
def this() = this(null, null, 0)
implicit object UserFormat extends Format[User] {
def reads(in : Input) = User(
read[Tuple2[String, String]](in),
read[String](in),
read[Int](in))
def writes(out: Output, value: User) = {
write[Tuple2[String, String]](out, value.usernamePassword)
write[String](out, value.email)
write[Int](out, value.age)
}
}
def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes)
def toBytes: Array[Byte] = toByteArray(this)
}