diff --git a/akka-docs/java/serialization.rst b/akka-docs/java/serialization.rst index 813db45a9a..0a41941ba5 100644 --- a/akka-docs/java/serialization.rst +++ b/akka-docs/java/serialization.rst @@ -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 { @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 { - @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 { + @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); diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst index 39f9304bc8..b229b57942 100644 --- a/akka-docs/scala/serialization.rst +++ b/akka-docs/scala/serialization.rst @@ -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 `_ 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. Akka’s Scala JSON is based upon the SJSON library. +Use the ``akka.serialization.Serializable.ScalaJSON`` base class with its toJSON method. Akka’s 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. Akka’s Java JSON is based upon the Jackson library. +Use the ``akka.serialization.Serializable.JavaJSON`` base class with its toJSONmethod. Akka’s 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) - }