Adding support for having method parameters individually serialized and deserialized using its own serializer, closing ticket #765

This commit is contained in:
Viktor Klang 2011-07-15 16:21:45 +02:00
parent c6297faa6f
commit 2cf64bccae
3 changed files with 34 additions and 8 deletions

View file

@ -10,6 +10,8 @@ import akka.dispatch.{ MessageDispatcher, Dispatchers, Future, FutureTimeoutExce
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
import akka.util.{ Duration }
import java.util.concurrent.atomic.{ AtomicReference AtomVar }
import akka.serialization.Serialization
import com.sun.xml.internal.ws.developer.MemberSubmissionAddressing.Validation
//TODO Document this class, not only in Scaladoc, but also in a dedicated typed-actor.rst, for both java and scala
/**
@ -87,16 +89,35 @@ object TypedActor {
}
} catch { case i: InvocationTargetException throw i.getTargetException }
private def writeReplace(): AnyRef = new SerializedMethodCall(method.getDeclaringClass, method.getName, method.getParameterTypes, parameters)
private def writeReplace(): AnyRef = {
val serializedParameters: Array[(Array[Byte],String)] = parameters match {
case null => null
case a if a.length == 0 => Array[(Array[Byte],String)]()
case a => a.map( {
case null => null
case value => Serialization.serializerFor(value.getClass).fold(throw _, s => (s.toBinary(value), s.getClass.getName))
})
}
new SerializedMethodCall(method.getDeclaringClass, method.getName, method.getParameterTypes, serializedParameters)
}
}
/**
* Represents the serialized form of a MethodCall, uses readResolve and writeReplace to marshall the call
*/
case class SerializedMethodCall(ownerType: Class[_], methodName: String, parameterTypes: Array[Class[_]], parameterValues: Array[AnyRef]) {
case class SerializedMethodCall(ownerType: Class[_], methodName: String, parameterTypes: Array[Class[_]], serializedParameters: Array[(Array[Byte],String)]) {
//TODO implement writeObject and readObject to serialize
//TODO Possible optimization is to special encode the parameter-types to conserve space
private def readResolve(): AnyRef = MethodCall(ownerType.getDeclaredMethod(methodName, parameterTypes: _*), parameterValues)
private def readResolve(): AnyRef = {
MethodCall(ownerType.getDeclaredMethod(methodName, parameterTypes: _*), serializedParameters match {
case null => null
case a if a.length == 0 => Array[AnyRef]()
case a => a.map( {
case null => null
case (bytes, serializerFQN) => Serialization.serializerOf(serializerFQN).fold(throw _, _.fromBinary(bytes))
})
})
}
}
/**

View file

@ -9,6 +9,7 @@ import akka.config.Config
import akka.config.Config._
import akka.actor.{ ActorRef, Actor }
import akka.AkkaException
import akka.util.ReflectiveAccess
case class NoSerializerFoundException(m: String) extends AkkaException(m)
@ -40,6 +41,12 @@ object Serialization {
case Left(e) => Left(e)
}
/**
* Tries to load the specified Serializer by the FQN
*/
def serializerOf(serializerFQN: String): Either[Exception, Serializer] =
createInstance(serializerFQN, ReflectiveAccess.emptyParams, ReflectiveAccess.emptyArguments)
private def serializerForBestMatchClass(cl: Class[_]): Either[Exception, Serializer] = {
if (bindings.isEmpty)
Left(NoSerializerFoundException("No mapping serializer found for " + cl))
@ -51,11 +58,7 @@ object Serialization {
case _ false
}
} map {
case (_, ser)
getClassFor(ser) match {
case Right(s) Right(s.newInstance.asInstanceOf[Serializer])
case _ Left(new Exception("Error instantiating " + ser))
}
case (_, ser) serializerOf(ser)
} getOrElse Left(NoSerializerFoundException("No mapping serializer found for " + cl))
}
}

View file

@ -23,6 +23,8 @@ import java.net.InetSocketAddress
object ReflectiveAccess {
val loader = getClass.getClassLoader
val emptyParams: Array[Class[_]] = Array()
val emptyArguments: Array[AnyRef] = Array()
/**
* Reflective access to the Cluster module.