diff --git a/akka-remote/src/main/scala/remote/MessageSerializer.scala b/akka-remote/src/main/scala/remote/MessageSerializer.scala
index 49f38524f9..7cda9e4b4c 100644
--- a/akka-remote/src/main/scala/remote/MessageSerializer.scala
+++ b/akka-remote/src/main/scala/remote/MessageSerializer.scala
@@ -28,17 +28,21 @@ object MessageSerializer extends Logging {
messageProtocol.getSerializationScheme match {
case SerializationSchemeType.JAVA =>
unbox(SERIALIZER_JAVA.fromBinary(messageProtocol.getMessage.toByteArray, None))
+
case SerializationSchemeType.PROTOBUF =>
val clazz = loadManifest(SERIALIZER_PROTOBUF.classLoader, messageProtocol)
SERIALIZER_PROTOBUF.fromBinary(messageProtocol.getMessage.toByteArray, Some(clazz))
+
case SerializationSchemeType.SBINARY =>
val clazz = loadManifest(SERIALIZER_SBINARY.classLoader, messageProtocol)
val renderer = clazz.newInstance.asInstanceOf[Serializable.SBinary[_ <: AnyRef]]
renderer.fromBytes(messageProtocol.getMessage.toByteArray)
+
case SerializationSchemeType.SCALA_JSON =>
val clazz = loadManifest(SERIALIZER_SCALA_JSON.classLoader, messageProtocol)
- import scala.reflect._
- SERIALIZER_SCALA_JSON.fromBinary(messageProtocol.getMessage.toByteArray)(Manifest.classType(clazz))
+ val renderer = clazz.newInstance.asInstanceOf[Serializable.ScalaJSON[_]]
+ renderer.fromBytes(messageProtocol.getMessage.toByteArray)
+
case SerializationSchemeType.JAVA_JSON =>
val clazz = loadManifest(SERIALIZER_JAVA_JSON.classLoader, messageProtocol)
SERIALIZER_JAVA_JSON.fromBinary(messageProtocol.getMessage.toByteArray, Some(clazz))
@@ -52,9 +56,9 @@ object MessageSerializer extends Logging {
builder.setSerializationScheme(SerializationSchemeType.PROTOBUF)
builder.setMessage(ByteString.copyFrom(serializable.toByteArray))
builder.setMessageManifest(ByteString.copyFromUtf8(serializable.getClass.getName))
- } else if (message.isInstanceOf[Serializable.ScalaJSON]) {
+ } else if (message.isInstanceOf[Serializable.ScalaJSON[_]]) {
builder.setSerializationScheme(SerializationSchemeType.SCALA_JSON)
- setMessageAndManifest(builder, message.asInstanceOf[Serializable.ScalaJSON])
+ setMessageAndManifest(builder, message.asInstanceOf[Serializable.ScalaJSON[_ <: Any]])
} else if (message.isInstanceOf[Serializable.SBinary[_]]) {
builder.setSerializationScheme(SerializationSchemeType.SBINARY)
setMessageAndManifest(builder, message.asInstanceOf[Serializable.SBinary[_ <: Any]])
diff --git a/akka-remote/src/main/scala/serialization/Serializable.scala b/akka-remote/src/main/scala/serialization/Serializable.scala
index 216869d7a6..c446dbbe59 100644
--- a/akka-remote/src/main/scala/serialization/Serializable.scala
+++ b/akka-remote/src/main/scala/serialization/Serializable.scala
@@ -91,10 +91,32 @@ object Serializable {
}
/**
+ * case class Address(street: String, city: String, zip: String)
+ * extends ScalaJSON[Address] {
+ *
+ * implicit val AddressFormat: Format[Address] =
+ * asProduct3("street", "city", "zip")(Address)(Address.unapply(_).get)
+ *
+ * import dispatch.json._
+ * import sjson.json._
+ * import sjson.json.JsonSerialization._
+ *
+ * def toJSON: String = JsValue.toJson(tojson(this))
+ * def toBytes: Array[Byte] = tobinary(this)
+ * def fromBytes(bytes: Array[Byte]): Address = frombinary[Address](bytes)
+ * def fromJSON(js: String): Address = fromjson[Address](Js(js))
+ * }
+ *
+ * val a = Address(...)
+ * val js = tojson(a)
+ * val add = fromjson[Address](js)
+ *
* @author Jonas Bonér
*/
- trait ScalaJSON extends JSON {
+ trait ScalaJSON[T] extends JSON {
def toJSON: String = new String(toBytes, "UTF-8")
- def toBytes: Array[Byte] = SJSONSerializer.SJSON.out(this)
+ def fromJSON(js: String): T
+ def toBytes: Array[Byte]
+ def fromBytes(bytes: Array[Byte]): T
}
}
diff --git a/akka-remote/src/main/scala/serialization/Serializer.scala b/akka-remote/src/main/scala/serialization/Serializer.scala
index 1365a7d4c1..d4950b323e 100644
--- a/akka-remote/src/main/scala/serialization/Serializer.scala
+++ b/akka-remote/src/main/scala/serialization/Serializer.scala
@@ -128,11 +128,25 @@ object Serializer {
/**
* @author Jonas Bonér
*/
- object ScalaJSON extends ScalaJSON
- trait ScalaJSON extends Serializer {
+ trait ScalaJSON {
+ import dispatch.json._
+ import sjson.json._
+ import sjson.json.JsonSerialization
+
+ var classLoader: Option[ClassLoader] = None
+
+ def tojson[T](o: T)(implicit tjs: Writes[T]): JsValue = JsonSerialization.tojson(o)(tjs)
+
+ def fromjson[T](json: JsValue)(implicit fjs: Reads[T]): T = JsonSerialization.fromjson(json)(fjs)
+
+ def tobinary[T](o: T)(implicit tjs: Writes[T]): Array[Byte] = JsonSerialization.tobinary(o)(tjs)
+
+ def frombinary[T](bytes: Array[Byte])(implicit fjs: Reads[T]): T = JsonSerialization.frombinary(bytes)(fjs)
+
+ // backward compatibility
+ // implemented using refelction based json serialization
def toBinary(obj: AnyRef): Array[Byte] = SJSONSerializer.SJSON.out(obj)
- // FIXME set ClassLoader on SJSONSerializer.SJSON
def fromBinary(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = SJSONSerializer.SJSON.in(bytes)
import scala.reflect.Manifest
@@ -144,6 +158,7 @@ object Serializer {
SJSONSerializer.SJSON.in(bytes)(m)
}
}
+ object ScalaJSON extends ScalaJSON
/**
* @author Jonas Bonér
diff --git a/akka-remote/src/test/scala/serialization/ScalaJSONSerializableSpec.scala b/akka-remote/src/test/scala/serialization/ScalaJSONSerializableSpec.scala
new file mode 100644
index 0000000000..0ca548d4e1
--- /dev/null
+++ b/akka-remote/src/test/scala/serialization/ScalaJSONSerializableSpec.scala
@@ -0,0 +1,67 @@
+package se.scalablesolutions.akka.serialization
+
+import org.scalatest.Spec
+import org.scalatest.matchers.ShouldMatchers
+import org.scalatest.BeforeAndAfterAll
+import org.scalatest.junit.JUnitRunner
+import org.junit.runner.RunWith
+
+import se.scalablesolutions.akka.serialization.Serializable.ScalaJSON
+
+object Serializables {
+ import sjson.json.DefaultProtocol._
+ case class Shop(store: String, item: String, price: Int) extends
+ ScalaJSON[Shop] {
+ implicit val ShopFormat: sjson.json.Format[Shop] =
+ asProduct3("store", "item", "price")(Shop)(Shop.unapply(_).get)
+
+ import dispatch.json._
+ import sjson.json._
+ import sjson.json.JsonSerialization._
+
+ def toBytes: Array[Byte] = tobinary(this)
+ def fromBytes(bytes: Array[Byte]) = frombinary[Shop](bytes)
+ def fromJSON(js: String) = fromjson[Shop](Js(js))
+ }
+
+ case class MyMessage(val id: String, val value: Tuple2[String, Int])
+ implicit val MyMessageFormat: sjson.json.Format[MyMessage] =
+ asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get)
+
+ case class MyJsonObject(val key: String, val map: Map[String, Int],
+ val standAloneInt: Int) extends ScalaJSON[MyJsonObject] {
+ implicit val MyJsonObjectFormat: sjson.json.Format[MyJsonObject] =
+ asProduct3("key", "map", "standAloneInt")(MyJsonObject)(MyJsonObject.unapply(_).get)
+
+ import dispatch.json._
+ import sjson.json._
+ import sjson.json.JsonSerialization._
+
+ def toBytes: Array[Byte] = tobinary(this)
+ def fromBytes(bytes: Array[Byte]) = frombinary[MyJsonObject](bytes)
+ def fromJSON(js: String) = fromjson[MyJsonObject](Js(js))
+ }
+}
+
+@RunWith(classOf[JUnitRunner])
+class ScalaJSONSerializableSpec extends
+ Spec with
+ ShouldMatchers with
+ BeforeAndAfterAll {
+
+ import Serializables._
+ describe("Serialization of case classes") {
+ it("should be able to serialize and de-serialize") {
+ val s = Shop("Target", "cooker", 120)
+ s.fromBytes(s.toBytes) should equal(s)
+ s.fromJSON(s.toJSON) should equal(s)
+
+ val key: String = "myKey"
+ val value: Int = 123
+ val standAloneInt: Int = 35
+ val message = MyJsonObject(key, Map(key -> value), standAloneInt)
+ message.fromBytes(message.toBytes) should equal(message)
+ message.fromJSON(message.toJSON) should equal(message)
+ }
+ }
+}
diff --git a/akka-remote/src/test/scala/serialization/ScalaJSONSerializerSpec.scala b/akka-remote/src/test/scala/serialization/ScalaJSONSerializerSpec.scala
new file mode 100644
index 0000000000..7d1ef4c7a0
--- /dev/null
+++ b/akka-remote/src/test/scala/serialization/ScalaJSONSerializerSpec.scala
@@ -0,0 +1,52 @@
+package se.scalablesolutions.akka.serialization
+
+import org.scalatest.Spec
+import org.scalatest.matchers.ShouldMatchers
+import org.scalatest.BeforeAndAfterAll
+import org.scalatest.junit.JUnitRunner
+import org.junit.runner.RunWith
+
+import se.scalablesolutions.akka.serialization.Serializer.ScalaJSON
+
+object Protocols {
+ import sjson.json.DefaultProtocol._
+ case class Shop(store: String, item: String, price: Int)
+ implicit val ShopFormat: sjson.json.Format[Shop] =
+ asProduct3("store", "item", "price")(Shop)(Shop.unapply(_).get)
+
+ case class MyMessage(val id: String, val value: Tuple2[String, Int])
+ implicit val MyMessageFormat: sjson.json.Format[MyMessage] =
+ asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get)
+
+ case class MyJsonObject(val key: String, val map: Map[String, Int],
+ val standAloneInt: Int)
+ implicit val MyJsonObjectFormat: sjson.json.Format[MyJsonObject] =
+ asProduct3("key", "map", "standAloneInt")(MyJsonObject)(MyJsonObject.unapply(_).get)
+}
+
+@RunWith(classOf[JUnitRunner])
+class ScalaJSONSerializerSpec extends
+ Spec with
+ ShouldMatchers with
+ BeforeAndAfterAll {
+
+ import Protocols._
+ import ScalaJSON._
+ describe("Serialization of case classes") {
+ it("should be able to serialize and de-serialize") {
+ val s = Shop("Target", "cooker", 120)
+ fromjson[Shop](tojson(s)) should equal(s)
+ frombinary[Shop](tobinary(s)) should equal(s)
+
+ val o = MyMessage("dg", ("akka", 100))
+ fromjson[MyMessage](tojson(o)) should equal(o)
+ frombinary[MyMessage](tobinary(o)) should equal(o)
+
+ val key: String = "myKey"
+ val value: Int = 123
+ val standAloneInt: Int = 35
+ val message = MyJsonObject(key, Map(key -> value), standAloneInt)
+ fromjson[MyJsonObject](tojson(message)) should equal(message)
+ }
+ }
+}
diff --git a/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala b/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala
index 832a655c22..de64b803fa 100644
--- a/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala
+++ b/akka-remote/src/test/scala/serialization/SerializableTypeClassActorSpec.scala
@@ -8,6 +8,7 @@ import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
import se.scalablesolutions.akka.serialization._
+import dispatch.json._
import se.scalablesolutions.akka.actor._
import ActorSerialization._
import Actor._
@@ -52,6 +53,10 @@ class SerializableTypeClassActorSpec extends
implicit object MyStatelessActorFormat extends StatelessActorFormat[MyStatelessActorWithMessagesInMailbox]
}
+ object BinaryFormatMyActorWithSerializableMessages {
+ implicit object MyActorWithSerializableMessagesFormat extends StatelessActorFormat[MyActorWithSerializableMessages]
+ }
+
object BinaryFormatMyJavaSerializableActor {
implicit object MyJavaSerializableActorFormat extends SerializerBasedActorFormat[MyJavaSerializableActor] {
val serializer = Serializer.Java
@@ -139,6 +144,29 @@ class SerializableTypeClassActorSpec extends
(actor3 !! "hello-reply").getOrElse("_") should equal("world")
}
}
+
+ describe("Custom serializable actors") {
+ it("should serialize and de-serialize") {
+ import BinaryFormatMyActorWithSerializableMessages._
+
+ val actor1 = actorOf[MyActorWithSerializableMessages].start
+ (actor1 ! MyMessage("hello1", ("akka", 100)))
+ (actor1 ! MyMessage("hello2", ("akka", 200)))
+ (actor1 ! MyMessage("hello3", ("akka", 300)))
+ (actor1 ! MyMessage("hello4", ("akka", 400)))
+ (actor1 ! MyMessage("hello5", ("akka", 500)))
+ actor1.mailboxSize should be > (0)
+ val actor2 = fromBinary(toBinary(actor1))
+ Thread.sleep(1000)
+ actor2.mailboxSize should be > (0)
+ (actor2 !! "hello-reply").getOrElse("_") should equal("world")
+
+ val actor3 = fromBinary(toBinary(actor1, false))
+ Thread.sleep(1000)
+ actor3.mailboxSize should equal(0)
+ (actor3 !! "hello-reply").getOrElse("_") should equal("world")
+ }
+ }
}
class MyActorWithDualCounter extends Actor {
@@ -188,3 +216,27 @@ class MyStatelessActorWithMessagesInMailbox extends Actor {
self.reply("world " + count)
}
}
+
+class MyActorWithSerializableMessages extends Actor {
+ def receive = {
+ case MyMessage(s, t) =>
+ println("# messages in mailbox " + self.mailboxSize)
+ Thread.sleep(500)
+ case "hello-reply" => self.reply("world")
+ }
+}
+
+case class MyMessage(val id: String, val value: Tuple2[String, Int])
+ extends Serializable.ScalaJSON[MyMessage] {
+
+ def this() = this(null, null)
+ import sjson.json.DefaultProtocol._
+ import sjson.json._
+ import sjson.json.JsonSerialization._
+ implicit val MyMessageFormat: sjson.json.Format[MyMessage] =
+ asProduct2("id", "value")(MyMessage)(MyMessage.unapply(_).get)
+
+ def toBytes: Array[Byte] = tobinary(this)
+ def fromBytes(bytes: Array[Byte]) = frombinary[MyMessage](bytes)
+ def fromJSON(js: String) = fromjson[MyMessage](Js(js))
+}
diff --git a/akka-remote/src/test/scala/serialization/Ticket436Spec.scala b/akka-remote/src/test/scala/serialization/Ticket436Spec.scala
deleted file mode 100644
index 042f3f07be..0000000000
--- a/akka-remote/src/test/scala/serialization/Ticket436Spec.scala
+++ /dev/null
@@ -1,49 +0,0 @@
-package se.scalablesolutions.akka.actor.serialization
-
-
-import org.scalatest.Spec
-import org.scalatest.matchers.ShouldMatchers
-import org.scalatest.BeforeAndAfterAll
-import org.scalatest.junit.JUnitRunner
-import org.junit.runner.RunWith
-
-import se.scalablesolutions.akka.serialization.Serializer
-import se.scalablesolutions.akka.serialization.Serializable.ScalaJSON
-import scala.reflect._
-import scala.annotation.target._
-import sjson.json.JSONTypeHint
-
-@BeanInfo class MyJsonObject(val key: String,
- @(JSONTypeHint @field)(value = classOf[Int])
- val map: Map[String, Int],
- val standAloneInt: Int) extends ScalaJSON {
- private def this() = this(null, null, -1)
- override def toString(): String = try {
- val mapValue: Int = map.getOrElse(key, -1)
- println("Map value: %s".format(mapValue.asInstanceOf[AnyRef].getClass))
- "Key: %s, Map value: %d, Stand Alone Int: %d".format(key, mapValue, standAloneInt)
- } catch {
- case e: ClassCastException => e.getMessage
- case _ => "Unknown error"
- }
-}
-
-@RunWith(classOf[JUnitRunner])
-class Ticket436Spec extends
- Spec with
- ShouldMatchers with
- BeforeAndAfterAll {
-
- describe("Serialization of Maps containing Int") {
- it("should be able to serialize and de-serialize preserving the data types of the Map") {
- val key: String = "myKey"
- val value: Int = 123
- val standAloneInt: Int = 35
- val message = new MyJsonObject(key, Map(key -> value), standAloneInt)
-
- val json = message.toJSON
- val copy = Serializer.ScalaJSON.fromJSON[MyJsonObject](json)
- copy.asInstanceOf[MyJsonObject].map.get("myKey").get.isInstanceOf[Int] should equal(true)
- }
- }
-}