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.
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
----------------------------------
@ -38,6 +16,8 @@ Step 1: Define the Actor
.. code-block:: scala
import akka.actor.UntypedActor;
public class SerializationTestActor extends UntypedActor {
public void onReceive(Object msg) {
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
import akka.serialization.StatelessActorFormat;
class SerializationTestActorFormat implements StatelessActorFormat<SerializationTestActor> {
@Override
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
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() {
ActorRef ref = Actors.actorOf(SerializationTestActor.class);
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
Here we consider an actor defined in Scala. We will however serialize using the Java APIs.
.. code-block:: scala
class MyUntypedActor extends UntypedActor {
var count = 0
def onReceive(message: Any): Unit = message match {
case m: String if m == "hello" =>
count = count + 1
getContext.replyUnsafe("world " + count)
case m: String =>
count = count + 1
getContext.replyUnsafe("hello " + m + " " + count)
case _ =>
throw new Exception("invalid message type")
import akka.actor.UntypedActor;
public class MyUntypedActor extends UntypedActor {
int count = 0;
public void onReceive(Object msg) {
if (msg.equals("hello")) {
count = count + 1;
getContext().replyUnsafe("world " + count);
} else if (msg instanceof String) {
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
class MyUntypedActorFormat implements Format<MyUntypedActor> {
@Override
public MyUntypedActor fromBinary(byte[] bytes, MyUntypedActor act) {
ProtobufProtocol.Counter p =
(ProtobufProtocol.Counter) new SerializerFactory().getProtobuf().fromBinary(bytes, ProtobufProtocol.Counter.class);
act.count_$eq(p.getCount());
return act;
}
import akka.actor.UntypedActor;
import akka.serialization.Format;
import akka.serialization.SerializerFactory;
@Override
public byte[] toBinary(MyUntypedActor ac) {
return ProtobufProtocol.Counter.newBuilder().setCount(ac.count()).build().toByteArray();
}
class MyUntypedActorFormat implements Format<MyUntypedActor> {
@Override
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
.. code-block:: java
import akka.actor.ActorRef;
import akka.actor.ActorTimeoutException;
import akka.actor.Actors;
import static akka.serialization.ActorSerialization.*;
@Test public void mustBeAbleToSerializeAStatefulActor() {
ActorRef ref = Actors.actorOf(MyUntypedActor.class);
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
import akka.serialization.{Serializer, Format}
import akka.actor.Actor
import akka.actor.Actor._
object BinaryFormatMyActor {
implicit object MyActorFormat extends Format[MyActor] {
def fromBinary(bytes: Array[Byte], act: MyActor) = {
@ -70,8 +75,7 @@ Step 2: Implement the type class for the actor
act
}
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
@serializable class MyJavaSerializableActor extends Actor {
class MyJavaSerializableActor extends Actor with scala.Serializable {
var count = 0
def receive = {
@ -173,6 +177,8 @@ Create a module for the type class ..
.. code-block:: scala
import akka.serialization.{SerializerBasedActorFormat, Serializer}
object BinaryFormatMyJavaSerializableActor {
implicit object MyJavaSerializableActorFormat extends SerializerBasedActorFormat[MyJavaSerializableActor] {
val serializer = Serializer.Java
@ -184,6 +190,7 @@ and serialize / de-serialize ..
.. code-block:: scala
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 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.
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.
@ -218,14 +225,14 @@ To deserialize the ``ActorRef`` to a ``RemoteActorRef`` you need to use the ``fr
.. code-block:: scala
import RemoteActorSerialization._
import akka.serialization.RemoteActorSerialization._
val actor2 = fromBinaryToRemoteActorRef(bytes)
You can also pass in a class loader to load the ``ActorRef`` class and dependencies from:
.. code-block:: scala
import RemoteActorSerialization._
import akka.serialization.RemoteActorSerialization._
val actor2 = fromBinaryToRemoteActorRef(bytes, classLoader)
Deep serialization of a TypedActor
@ -240,6 +247,8 @@ Step 1: Define the actor
.. code-block:: scala
import akka.actor.TypedActor
trait MyTypedActor {
def requestReply(s: String) : String
def oneWay() : Unit
@ -252,12 +261,18 @@ Step 1: Define the actor
count = count + 1
"world " + count
}
override def oneWay() {
count = count + 1
}
}
Step 2: Implement the type class for the actor
.. code-block:: scala
import akka.serialization.{Serializer, Format}
class MyTypedActorFormat extends Format[MyTypedActorImpl] {
def fromBinary(bytes: Array[Byte], act: MyTypedActorImpl) = {
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
import akka.serialization.TypedActorSerialization._
val typedActor1 = TypedActor.newInstance(classOf[MyTypedActor], classOf[MyTypedActorImpl], 1000)
val f = new MyTypedActorFormat
@ -288,7 +305,7 @@ To deserialize the TypedActor to a ``RemoteTypedActorRef`` (an aspectwerkz proxy
.. code-block:: scala
import RemoteTypedActorSerialization._
import akka.serialization.RemoteTypedActorSerialization._
val typedActor = fromBinaryToRemoteTypedActorRef(bytes)
// 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)
- JavaJSON (Java but some Scala structures)
- SBinary (Scala only)
- 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.
@ -377,15 +393,16 @@ The remote Actor can then receive the Protobuf message typed as-is:
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.
.. code-block:: scala
import akka.serialization.Serializer
import akka.serialization._
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] {
// type class instance
@ -427,7 +444,7 @@ Here are the steps that you need to follow:
.. code-block:: scala
import DefaultProtocol._
import akka.serialization.DefaultProtocol._
implicit val MyMessageFormat: sjson.json.Format[MyMessage] =
asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get)
@ -436,6 +453,7 @@ Here are the steps that you need to follow:
.. code-block:: scala
import akka.serialization.Serializer.ScalaJSON
import akka.serialization.JsonSerialization._
val o = MyMessage("dg", ("akka", 100))
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.
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
@ -900,12 +918,15 @@ There are other nifty ways to implement case class serialization using sjson. Fo
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
import akka.serialization.Serializable.JavaJSON;
import akka.serialization.SerializerFactory;
class MyMessage extends JavaJSON {
private String name = null;
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);
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)
}