Merge branch 'master' of github.com:jboner/akka
This commit is contained in:
commit
11b5732b0c
7 changed files with 220 additions and 58 deletions
|
|
@ -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]])
|
||||
|
|
|
|||
|
|
@ -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 <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,11 +128,24 @@ object Serializer {
|
|||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object ScalaJSON extends ScalaJSON
|
||||
trait ScalaJSON extends Serializer {
|
||||
trait ScalaJSON {
|
||||
import dispatch.json._
|
||||
import sjson.json._
|
||||
|
||||
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 +157,7 @@ object Serializer {
|
|||
SJSONSerializer.SJSON.in(bytes)(m)
|
||||
}
|
||||
}
|
||||
object ScalaJSON extends ScalaJSON
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue