completed protobuf protocol for remoting

This commit is contained in:
jboner 2009-07-18 00:16:32 +02:00
parent a4d22af64b
commit f26110e55c
23 changed files with 795 additions and 385 deletions

View file

@ -57,6 +57,7 @@
<orderEntry type="library" exported="" name="Maven: org.apache:zookeeper:3.1.0" level="project" />
<orderEntry type="library" exported="" name="Maven: org.codehaus.jackson:jackson-core-asl:1.1.0" level="project" />
<orderEntry type="library" exported="" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.1.0" level="project" />
<orderEntry type="library" exported="" name="Maven: sbinary:sbinary:0.3-alpha" level="project" />
<orderEntry type="library" exported="" name="Maven: org.apache.cassandra:cassandra:0.4.0-dev" level="project" />
<orderEntry type="library" exported="" name="Maven: com.facebook:thrift:1.0" level="project" />
<orderEntry type="library" exported="" name="Maven: com.facebook:fb303:1.0" level="project" />
@ -82,7 +83,6 @@
<orderEntry type="library" exported="" name="Maven: org.slf4j:slf4j-api:1.4.3" level="project" />
<orderEntry type="library" exported="" name="Maven: log4j:log4j:1.2.13" level="project" />
<orderEntry type="library" name="Maven: org.scala-tools.testing:scalatest:0.9.5" level="project" />
<orderEntry type="library" name="Maven: com.jteigen.scalatest:junit4runner:1.0" level="project" />
<orderEntry type="library" exported="" name="Maven: com.sun.jersey:jersey-client:1.1.0-ea" level="project" />
</component>
</module>

View file

@ -76,6 +76,11 @@
<artifactId>protobuf-java</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>sbinary</groupId>
<artifactId>sbinary</artifactId>
<version>0.3</version>
</dependency>
<!-- For Cassandra -->
<dependency>
@ -155,12 +160,12 @@
<version>0.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<!--dependency>
<groupId>com.jteigen.scalatest</groupId>
<artifactId>junit4runner</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
</dependency-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View file

@ -4,16 +4,16 @@
package se.scalablesolutions.akka.kernel.actor
import com.google.protobuf.ByteString
import java.io.File
import java.lang.reflect.{InvocationTargetException, Method}
import java.net.InetSocketAddress
import kernel.config.ScalaConfig._
import kernel.reactor.{MessageDispatcher, FutureResult}
import kernel.util.{HashCode, Serializer, JSONSerializer}
import kernel.nio.RemoteRequestIdFactory
import kernel.config.JavaConfig.RestartCallbacks
import kernel.nio.protobuf.RemoteProtocol.RemoteRequest
import kernel.util.{HashCode, Serializer, JavaJSONSerializer}
import kernel.nio.protobuf.RemoteProtocol.{RemoteRequest, RemoteReply}
import kernel.nio.{RemoteClient, RemoteServer, RemoteRequestIdFactory}
import org.codehaus.aspectwerkz.intercept.{Advisable, AroundAdvice}
import org.codehaus.aspectwerkz.joinpoint.{MethodRtti, JoinPoint}
@ -38,8 +38,6 @@ object Annotations {
*/
class ActiveObjectFactory {
protected[this] val serializer: Serializer = JSONSerializer
// FIXME How to pass the MessageDispatcher on from active object to child???????
def newInstance[T](target: Class[T], timeout: Long): T =
@ -257,6 +255,8 @@ sealed class ActorAroundAdvice(val target: Class[_],
val actor: Dispatcher,
val remoteAddress: Option[InetSocketAddress],
val timeout: Long) extends AroundAdvice {
private val serializer: Serializer = JavaJSONSerializer
val id = target.getName
actor.timeout = timeout
actor.start
@ -281,19 +281,24 @@ sealed class ActorAroundAdvice(val target: Class[_],
private def remoteDispatch(joinpoint: JoinPoint): AnyRef = {
val rtti = joinpoint.getRtti.asInstanceOf[MethodRtti]
val oneWay = isOneWay(rtti)
val message = rtti.getParameterValues
val (message: AnyRef, isEscaped) = escapeArguments(rtti.getParameterValues)
val supervisorId = {
val id = actor.registerSupervisorAsRemoteActor
if (id.isDefined) id.get
else null
}
val request = RemoteRequest.newBuilder
.setId(RemoteRequestIdFactory.nextId)
.setMessage(serializer.out(message))
.setMessageType(message.getClass.getName)
.setMethod(rtti.getMethod.getName)
.setTarget(target.getName)
.setTimeout(timeout)
.setSupervisorUuid(actor.registerSupervisorAsRemoteActor)
.setIsActor(false)
.setIsOneWay(oneWay)
.setIsEscaped(false)
.build
.setId(RemoteRequestIdFactory.nextId)
.setMessage(ByteString.copyFrom(serializer.out(message)))
.setMessageType(message.getClass.getName)
.setMethod(rtti.getMethod.getName)
.setTarget(target.getName)
.setTimeout(timeout)
.setSupervisorUuid(supervisorId)
.setIsActor(false)
.setIsOneWay(oneWay)
.setIsEscaped(false)
.build
val future = RemoteClient.clientFor(remoteAddress.get).send(request)
if (oneWay) null // for void methods
else {
@ -315,6 +320,18 @@ sealed class ActorAroundAdvice(val target: Class[_],
private def isOneWay(rtti: MethodRtti) =
rtti.getMethod.getReturnType == java.lang.Void.TYPE ||
rtti.getMethod.isAnnotationPresent(Annotations.oneway)
private def escapeArguments(args: Array[Object]): Tuple2[Array[Object], Boolean] = {
var isEscaped = false
val escapedArgs = for (arg <- args) yield {
val clazz = arg.getClass
if (clazz.getName.contains("$$ProxiedByAW")) {
isEscaped = true
"$$ProxiedByAW" + clazz.getSuperclass.getName
} else arg
}
(escapedArgs, isEscaped)
}
}
/**
@ -414,12 +431,14 @@ private[kernel] class Dispatcher(val callbacks: Option[RestartCallbacks]) extend
var hasMutableArgument = false
for (arg <- args.toList) {
if (!arg.isInstanceOf[String] &&
!arg.isInstanceOf[Byte] &&
!arg.isInstanceOf[Int] &&
!arg.isInstanceOf[Long] &&
!arg.isInstanceOf[Float] &&
!arg.isInstanceOf[Double] &&
!arg.isInstanceOf[Boolean] &&
!arg.isInstanceOf[Char] &&
!arg.isInstanceOf[java.lang.Byte] &&
!arg.isInstanceOf[java.lang.Integer] &&
!arg.isInstanceOf[java.lang.Long] &&
!arg.isInstanceOf[java.lang.Float] &&

View file

@ -4,6 +4,7 @@
package se.scalablesolutions.akka.kernel.actor
import com.google.protobuf.ByteString
import java.net.InetSocketAddress
import java.util.concurrent.CopyOnWriteArraySet
@ -11,9 +12,10 @@ import kernel.reactor._
import kernel.config.ScalaConfig._
import kernel.stm.TransactionManagement
import kernel.util.Helpers.ReadWriteLock
import kernel.util.{Serializer, JSONSerializer, Logging}
import kernel.nio._
import kernel.util.{Serializer, ScalaJSONSerializer, Logging}
import kernel.nio.protobuf._
import kernel.nio.{RemoteServer, RemoteClient, RemoteRequestIdFactory}
import nio.protobuf.RemoteProtocol.RemoteRequest
sealed abstract class LifecycleMessage
@ -34,6 +36,10 @@ class ActorMessageInvoker(val actor: Actor) extends MessageInvoker {
def invoke(handle: MessageInvocation) = actor.invoke(handle)
}
def deserialize(array : Array[Byte]) : MediaContent = fromByteArray[MediaContent](array)
def serialize(content : MediaContent) : Array[Byte] = toByteArray(content)
object Actor {
val TIMEOUT = kernel.Kernel.config.getInt("akka.actor.timeout", 5000)
val SERIALIZE_MESSAGES = kernel.Kernel.config.getBool("akka.actor.serialize-messages", false)
@ -44,6 +50,7 @@ trait Actor extends Logging with TransactionManagement {
private[this] val remoteFlagLock = new ReadWriteLock
private[this] val transactionalFlagLock = new ReadWriteLock
private var hotswap: Option[PartialFunction[Any, Unit]] = None
private var config: Option[AnyRef] = None
@volatile protected[this] var isTransactional = false
@ -54,7 +61,7 @@ trait Actor extends Logging with TransactionManagement {
protected[this] val linkedActors = new CopyOnWriteArraySet[Actor]
protected[actor] var lifeCycleConfig: Option[LifeCycle] = None
protected[this] val serializer: Serializer = JSONSerializer
protected[this] val serializer: Serializer = ScalaJSONSerializer
// ====================================
// ==== USER CALLBACKS TO OVERRIDE ====
@ -312,6 +319,7 @@ trait Actor extends Logging with TransactionManagement {
if (!linkedActors.contains(actor)) throw new IllegalStateException("Actor [" + actor + "] is not a linked actor, can't unlink")
linkedActors.remove(actor)
actor.supervisor = None
log.debug("Unlinking actor [%s] from actor [%s]", actor, this)
} else throw new IllegalStateException("Actor has not been started, you need to invoke 'actor.start' before using it")
}
@ -393,19 +401,18 @@ trait Actor extends Logging with TransactionManagement {
private def postMessageToMailbox(message: AnyRef): Unit = remoteFlagLock.withReadLock { // the price you pay for being able to make an actor remote at runtime
if (remoteAddress.isDefined) {
val request = RemoteRequest.newBuilder
.setId(RemoteRequestIdFactory.nextId)
.setMessage(serializer.out(message))
.setMessageType(message.getClass.getName)
.setMethod(null)
.setTarget(this.getClass.getName)
.setTimeout(timeout)
.setSupervisorUuid(registerSupervisorAsRemoteActor)
.setIsActor(true)
.setIsOneWay(true)
.setIsEscaped(false)
.build
RemoteClient.clientFor(remoteAddress.get).send(request)
val requestBuilder = RemoteRequest.newBuilder
.setId(RemoteRequestIdFactory.nextId)
.setMessage(ByteString.copyFrom(serializer.out(message)))
.setMessageType(message.getClass.getName)
.setTarget(this.getClass.getName)
.setTimeout(timeout)
.setIsActor(true)
.setIsOneWay(true)
.setIsEscaped(false)
val id = registerSupervisorAsRemoteActor
if (id.isDefined) requestBuilder.setSupervisorUuid(id.get)
RemoteClient.clientFor(remoteAddress.get).send(requestBuilder.build)
} else {
val handle = new MessageInvocation(this, message, None, TransactionManagement.threadBoundTx.get)
mailbox.append(handle)
@ -415,19 +422,18 @@ trait Actor extends Logging with TransactionManagement {
private def postMessageToMailboxAndCreateFutureResultWithTimeout(message: AnyRef, timeout: Long): CompletableFutureResult = remoteFlagLock.withReadLock { // the price you pay for being able to make an actor remote at runtime
if (remoteAddress.isDefined) {
val request = RemoteRequest.newBuilder
.setId(RemoteRequestIdFactory.nextId)
.setMessage(serializer.out(message))
.setMethod(null)
.setMessageType(message.getClass.getName)
.setTarget(this.getClass.getName)
.setTimeout(timeout)
.setSupervisorUuid(registerSupervisorAsRemoteActor)
.setIsActor(true)
.setIsOneWay(false)
.setIsEscaped(false)
.build
val future = RemoteClient.clientFor(remoteAddress.get).send(request)
val requestBuilder = RemoteRequest.newBuilder
.setId(RemoteRequestIdFactory.nextId)
.setMessage(ByteString.copyFrom(serializer.out(message)))
.setMessageType(message.getClass.getName)
.setTarget(this.getClass.getName)
.setTimeout(timeout)
.setIsActor(true)
.setIsOneWay(false)
.setIsEscaped(false)
val id = registerSupervisorAsRemoteActor
if (id.isDefined) requestBuilder.setSupervisorUuid(id.get)
val future = RemoteClient.clientFor(remoteAddress.get).send(requestBuilder.build)
if (future.isDefined) future.get
else throw new IllegalStateException("Expected a future from remote call to actor " + toString)
} else {
@ -579,25 +585,31 @@ trait Actor extends Logging with TransactionManagement {
dispatcher.registerHandler(this, new ActorMessageInvoker(this))
}
/*
private def serializeMessage(message: AnyRef): AnyRef = if (Actor.SERIALIZE_MESSAGES) {
if (!message.isInstanceOf[String] &&
!message.isInstanceOf[Byte] &&
!message.isInstanceOf[Int] &&
!message.isInstanceOf[Long] &&
!message.isInstanceOf[Float] &&
!message.isInstanceOf[Double] &&
!message.isInstanceOf[Boolean] &&
!message.isInstanceOf[Char] &&
!message.isInstanceOf[java.lang.Integer] &&
!message.isInstanceOf[java.lang.Long] &&
!message.isInstanceOf[java.lang.Float] &&
!message.isInstanceOf[java.lang.Double] &&
!message.isInstanceOf[java.lang.Boolean] &&
!message.isInstanceOf[java.lang.Character] &&
!message.isInstanceOf[Tuple2[_,_]] &&
!message.isInstanceOf[Tuple3[_,_,_]] &&
!message.isInstanceOf[Tuple4[_,_,_,_]] &&
!message.isInstanceOf[Tuple5[_,_,_,_,_]] &&
!message.isInstanceOf[Tuple6[_,_,_,_,_,_]] &&
!message.isInstanceOf[Tuple7[_,_,_,_,_,_,_]] &&
!message.isInstanceOf[Tuple8[_,_,_,_,_,_,_,_]] &&
!message.isInstanceOf[Array[_]] &&
!message.isInstanceOf[List[_]] &&
!message.isInstanceOf[scala.collection.immutable.Map[_,_]] &&
!message.isInstanceOf[scala.collection.immutable.Set[_]] &&
!message.isInstanceOf[scala.collection.immutable.Tree[_,_]] &&
!message.getClass.isAnnotationPresent(Annotations.immutable)) {
serializer.deepClone(message)
} else message
} else message
*/
override def toString(): String = "Actor[" + uuid + ":" + id + "]"
}

View file

@ -7,15 +7,18 @@ package se.scalablesolutions.akka.kernel.nio
import java.net.InetSocketAddress
import java.util.concurrent.{Executors, ConcurrentMap, ConcurrentHashMap}
import kernel.nio.protobuf.RemoteProtocol.{RemoteRequest, RemoteReply}
import kernel.actor.{Exit, Actor}
import kernel.reactor.{DefaultCompletableFutureResult, CompletableFutureResult}
import kernel.util.{JSONSerializer, Logging}
import kernel.util.{Serializer, ScalaJSONSerializer, JavaJSONSerializer, Logging}
import org.jboss.netty.bootstrap.ClientBootstrap
import org.jboss.netty.channel._
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory
import org.jboss.netty.handler.codec.serialization.{ObjectEncoder, ObjectDecoder}
import org.jboss.netty.bootstrap.ClientBootstrap
import org.jboss.netty.handler.codec.frame.{LengthFieldBasedFrameDecoder, LengthFieldPrepender}
import org.jboss.netty.handler.codec.protobuf.{ProtobufDecoder, ProtobufEncoder}
import protobuf.RemoteProtocol.RemoteReply
import protobuf.RemoteProtocol
import scala.collection.mutable.HashMap
object RemoteClient extends Logging {
@ -45,9 +48,8 @@ class RemoteClient(hostname: String, port: Int) extends Logging {
Executors.newCachedThreadPool)
private val bootstrap = new ClientBootstrap(channelFactory)
private val handler = new RemoteClientHandler(futures, supervisors)
bootstrap.getPipeline.addLast("handler", handler)
bootstrap.setPipelineFactory(new RemoteClientPipelineFactory(futures, supervisors))
bootstrap.setOption("tcpNoDelay", true)
bootstrap.setOption("keepAlive", true)
@ -76,15 +78,14 @@ class RemoteClient(hostname: String, port: Int) extends Logging {
}
def send(request: RemoteRequest): Option[CompletableFutureResult] = if (isRunning) {
val escapedRequest = request//escapeRequest(request)
if (escapedRequest.isOneWay) {
connection.getChannel.write(escapedRequest)
if (request.getIsOneWay) {
connection.getChannel.write(request)
None
} else {
futures.synchronized {
val futureResult = new DefaultCompletableFutureResult(request.timeout)
futures.put(request.id, futureResult)
connection.getChannel.write(escapedRequest)
val futureResult = new DefaultCompletableFutureResult(request.getTimeout)
futures.put(request.getId, futureResult)
connection.getChannel.write(request)
Some(futureResult)
}
}
@ -99,20 +100,18 @@ class RemoteClient(hostname: String, port: Int) extends Logging {
else supervisors.remove(actor.supervisor.get.uuid)
def deregisterSupervisorWithUuid(uuid: String) = supervisors.remove(uuid)
}
private def escapeRequest(request: RemoteRequest) = {
if (request.message.isInstanceOf[Array[Object]]) {
val args = request.message.asInstanceOf[Array[Object]].toList.asInstanceOf[scala.List[Object]]
var isEscaped = false
val escapedArgs = for (arg <- args) yield {
val clazz = arg.getClass
if (clazz.getName.contains("$$ProxiedByAW")) {
isEscaped = true
new ProxyWrapper(clazz.getSuperclass.getName)
} else arg
}
request.cloneWithNewMessage(escapedArgs, isEscaped)
} else request
class RemoteClientPipelineFactory(futures: ConcurrentMap[Long, CompletableFutureResult],
supervisors: ConcurrentMap[String, Actor]) extends ChannelPipelineFactory {
def getPipeline: ChannelPipeline = {
val p = Channels.pipeline()
p.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
p.addLast("protobufDecoder", new ProtobufDecoder(RemoteProtocol.RemoteReply.getDefaultInstance));
p.addLast("frameEncoder", new LengthFieldPrepender(4));
p.addLast("protobufEncoder", new ProtobufEncoder());
p.addLast("handler", new RemoteClientHandler(futures, supervisors))
p
}
}
@ -128,41 +127,36 @@ class RemoteClientHandler(val futures: ConcurrentMap[Long, CompletableFutureResu
super.handleUpstream(ctx, event)
}
override def channelOpen(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
//event.getChannel.getPipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
//event.getChannel.getPipeline.addLast("protobufDecoder", new ProtobufDecoder(LocalTimeProtocol.LocalTimes.getDefaultInstance()));
//event.getChannel.getPipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
//event.getChannel.getPipeline.addLast("protobufEncoder", new ProtobufEncoder());
event.getChannel.getPipeline.addFirst("encoder", new ObjectEncoder)
event.getChannel.getPipeline.addFirst("decoder", new ObjectDecoder)
}
override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) {
// Send the first message if this handler is a client-side handler.
// if (!firstMessage.isEmpty) e.getChannel.write(firstMessage)
}
override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) {
try {
val result = event.getMessage
if (result.isInstanceOf[RemoteReply]) {
val reply = result.asInstanceOf[RemoteReply]
log.debug("Received RemoteReply[\n%s]", reply.toString)
val future = futures.get(reply.getId)
val messageBytes = reply.getMessage
val messageType = reply.getMessageType
val messageClass = Class.forName(messageType)
val message = JSONSerializer.in(messageBytes, messageClass)
if (reply.successful) future.completeWithResult(message)
else {
val supervisorUuid = reply.getSupervisorUuid
if (supervisorUuid != null) {
if (reply.getIsSuccessful) {
val messageBytes = reply.getMessage.toByteArray
val messageType = reply.getMessageType
val messageClass = Class.forName(messageType)
val message =
if (reply.isActor) ScalaJSONSerializer.in(messageBytes, Some(messageClass))
else JavaJSONSerializer.in(messageBytes, Some(messageClass))
future.completeWithResult(message)
} else {
if (reply.hasSupervisorUuid) {
val supervisorUuid = reply.getSupervisorUuid
if (!supervisors.containsKey(supervisorUuid)) throw new IllegalStateException("Expected a registered supervisor for UUID [" + supervisorUuid + "] but none was found")
val supervisedActor = supervisors.get(supervisorUuid)
if (!supervisedActor.supervisor.isDefined) throw new IllegalStateException("Can't handle restart for remote actor " + supervisedActor + " since its supervisor has been removed")
else supervisedActor.supervisor.get ! Exit(supervisedActor, new RuntimeException(reply.getException))
}
future.completeWithException(null, new RuntimeException(reply.getException))
val exception = reply.getException
val exceptionType = Class.forName(exception.substring(0, exception.indexOf('$')))
val exceptionMessage = exception.substring(exception.indexOf('$') + 1, exception.length)
val exceptionInstance = exceptionType
.getConstructor(Array[Class[_]](classOf[String]): _*)
.newInstance(exceptionMessage).asInstanceOf[Throwable]
future.completeWithException(null, exceptionInstance)
}
futures.remove(reply.getId)
} else throw new IllegalArgumentException("Unknown message received in remote client handler: " + result)
@ -175,6 +169,7 @@ class RemoteClientHandler(val futures: ConcurrentMap[Long, CompletableFutureResu
override def exceptionCaught(ctx: ChannelHandlerContext, event: ExceptionEvent) {
log.error("Unexpected exception from downstream in remote client: %s", event.getCause)
event.getCause.printStackTrace
event.getChannel.close
}
}

View file

@ -9,18 +9,18 @@ import java.net.InetSocketAddress
import java.util.concurrent.{ConcurrentHashMap, Executors}
import kernel.actor._
import kernel.stm.TransactionManagement
import kernel.util.{JSONSerializer, Logging}
import kernel.util.{Serializer, ScalaJSONSerializer, JavaJSONSerializer, Logging}
import protobuf.RemoteProtocol
import protobuf.RemoteProtocol.{RemoteReply, RemoteRequest}
import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.channel._
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
import org.jboss.netty.handler.codec.frame.{LengthFieldBasedFrameDecoder, LengthFieldPrepender}
import org.jboss.netty.handler.codec.protobuf.{ProtobufDecoder, ProtobufEncoder}
import org.jboss.netty.handler.codec.serialization.ObjectDecoder
import org.jboss.netty.handler.codec.serialization.ObjectEncoder
import protobuf.RemoteProtocol.{RemoteReply, RemoteRequest}
import com.google.protobuf.ByteString
class RemoteServer extends Logging {
def start = RemoteServer.start
}
@ -41,8 +41,8 @@ object RemoteServer extends Logging {
private val bootstrap = new ServerBootstrap(factory)
// FIXME provide different codecs (Thrift, Avro, Protobuf, JSON)
private val handler = new AkkaServerHandler
bootstrap.getPipeline.addLast("handler", handler)
private val handler = new RemoteServerHandler
bootstrap.setPipelineFactory(new RemoteServerPipelineFactory)
bootstrap.setOption("child.tcpNoDelay", true)
bootstrap.setOption("child.keepAlive", true)
bootstrap.setOption("child.reuseAddress", true)
@ -57,12 +57,24 @@ object RemoteServer extends Logging {
}
}
class RemoteServerPipelineFactory extends ChannelPipelineFactory {
def getPipeline: ChannelPipeline = {
val p = Channels.pipeline()
p.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4))
p.addLast("protobufDecoder", new ProtobufDecoder(RemoteProtocol.RemoteRequest.getDefaultInstance))
p.addLast("frameEncoder", new LengthFieldPrepender(4))
p.addLast("protobufEncoder", new ProtobufEncoder)
p.addLast("handler", new RemoteServerHandler)
p
}
}
@ChannelPipelineCoverage { val value = "all" }
class AkkaServerHandler extends SimpleChannelUpstreamHandler with Logging {
class RemoteServerHandler extends SimpleChannelUpstreamHandler with Logging {
private val activeObjectFactory = new ActiveObjectFactory
private val activeObjects = new ConcurrentHashMap[String, AnyRef]
private val actors = new ConcurrentHashMap[String, Actor]
override def handleUpstream(ctx: ChannelHandlerContext, event: ChannelEvent) = {
if (event.isInstanceOf[ChannelStateEvent] && event.asInstanceOf[ChannelStateEvent].getState != ChannelState.INTEREST_OPS) {
log.debug(event.toString)
@ -70,20 +82,6 @@ class AkkaServerHandler extends SimpleChannelUpstreamHandler with Logging {
super.handleUpstream(ctx, event)
}
override def channelOpen(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
//event.getChannel.getPipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
//event.getChannel.getPipeline.addLast("protobufDecoder", new ProtobufDecoder(LocalTimeProtocol.LocalTimes.getDefaultInstance()));
//event.getChannel.getPipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
//event.getChannel.getPipeline.addLast("protobufEncoder", new ProtobufEncoder());
event.getChannel.getPipeline.addFirst("encoder", new ObjectEncoder)
event.getChannel.getPipeline.addFirst("decoder", new ObjectDecoder)
}
override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
//e.getChannel.write(firstMessage)
}
override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) = {
val message = event.getMessage
if (message == null) throw new IllegalStateException("Message in remote MessageEvent is null: " + event)
@ -91,82 +89,100 @@ class AkkaServerHandler extends SimpleChannelUpstreamHandler with Logging {
}
override def exceptionCaught(ctx: ChannelHandlerContext, event: ExceptionEvent) = {
event.getCause.printStackTrace
log.error("Unexpected exception from remote downstream: %s", event.getCause)
event.getCause.printStackTrace
event.getChannel.close
}
private def handleRemoteRequest(request: RemoteRequest, channel: Channel) = {
log.debug(request.toString)
if (request.isActor) dispatchToActor(request, channel)
log.debug("Received RemoteRequest[\n%s]", request.toString)
if (request.getIsActor) dispatchToActor(request, channel)
else dispatchToActiveObject(request, channel)
}
private def dispatchToActor(request: RemoteRequest, channel: Channel) = {
log.debug("Dispatching to remote actor [%s]", request.target)
val actor = createActor(request.target, request.timeout)
log.debug("Dispatching to remote actor [%s]", request.getTarget)
val actor = createActor(request.getTarget, request.getTimeout)
actor.start
val messageBytes = request.getMessage
val messageType = request.getMessageType
val messageClass = Class.forName(messageType)
val message = JSONSerializer.in(messageBytes, messageClass)
if (request.isOneWay) actor ! message
val messageClass = Class.forName(request.getMessageType)
val message = ScalaJSONSerializer.in(request.getMessage.toByteArray, Some(messageClass))
if (request.getIsOneWay) actor ! message
else {
try {
val resultOrNone = actor !! message
val result: AnyRef = if (resultOrNone.isDefined) resultOrNone.get else null
log.debug("Returning result from actor invocation [%s]", result)
val replyMessage = JSONSerializer.out(result)
val reply = RemoteReply.newBuilder
.setId(request.getId)
.setMessage(replyMessage)
.setMessageType(result.getClass.getName)
.setIsSuccessful(true)
.setSupervisorUuid(request.getSupervisorUuid)
.build
channel.write(reply)
val replyMessage = ScalaJSONSerializer.out(result)
val replyBuilder = RemoteReply.newBuilder
.setId(request.getId)
.setMessage(ByteString.copyFrom(replyMessage))
.setMessageType(result.getClass.getName)
.setIsSuccessful(true)
.setIsActor(true)
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
channel.write(replyBuilder.build)
} catch {
case e: Throwable =>
log.error("Could not invoke remote actor [%s] due to: %s", request.target, e)
log.error("Could not invoke remote actor [%s] due to: %s", request.getTarget, e)
e.printStackTrace
val reply = RemoteReply.newBuilder
.setId(request.getId)
.setException(e.toString)
.setIsSuccessful(false)
.setSupervisorUuid(request.getSupervisorUuid)
.build
channel.write(reply)
val replyBuilder = RemoteReply.newBuilder
.setId(request.getId)
.setException(e.getClass.getName + "$" + e.getMessage)
.setIsSuccessful(false)
.setIsActor(true)
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
channel.write(replyBuilder.build)
}
}
}
private def dispatchToActiveObject(request: RemoteRequest, channel: Channel) = {
log.debug("Dispatching to remote active object [%s :: %s]", request.method, request.target)
val activeObject = createActiveObject(request.target, request.timeout)
log.debug("Dispatching to remote active object [%s :: %s]", request.getMethod, request.getTarget)
val activeObject = createActiveObject(request.getTarget, request.getTimeout)
val args = request.message.asInstanceOf[scala.List[AnyRef]]
val args: scala.List[AnyRef] = JavaJSONSerializer.in(request.getMessage.toByteArray, Some(classOf[scala.List[AnyRef]]))
val argClasses = args.map(_.getClass)
val (unescapedArgs, unescapedArgClasses) = unescapeArgs(args, argClasses, request.timeout)
val (unescapedArgs, unescapedArgClasses) = unescapeArgs(args, argClasses, request.getTimeout)
//continueTransaction(request)
try {
val messageReceiver = activeObject.getClass.getDeclaredMethod(request.method, unescapedArgClasses: _*)
if (request.isOneWay) messageReceiver.invoke(activeObject, unescapedArgs: _*)
val messageReceiver = activeObject.getClass.getDeclaredMethod(request.getMethod, unescapedArgClasses: _*)
if (request.getIsOneWay) messageReceiver.invoke(activeObject, unescapedArgs: _*)
else {
val result = messageReceiver.invoke(activeObject, unescapedArgs: _*)
log.debug("Returning result from remote active object invocation [%s]", result)
//channel.write(request.newReplyWithMessage(result, TransactionManagement.threadBoundTx.get))
channel.write(request.newReplyWithMessage(result, null))
val replyMessage = JavaJSONSerializer.out(result)
val replyBuilder = RemoteReply.newBuilder
.setId(request.getId)
.setMessage(ByteString.copyFrom(replyMessage))
.setMessageType(result.getClass.getName)
.setIsSuccessful(true)
.setIsActor(false)
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
channel.write(replyBuilder.build)
}
} catch {
case e: InvocationTargetException =>
log.error("Could not invoke remote active object [%s :: %s] due to: %s", request.method, request.target, e.getCause)
log.error("Could not invoke remote active object [%s :: %s] due to: %s", request.getMethod, request.getTarget, e.getCause)
e.getCause.printStackTrace
channel.write(request.newReplyWithException(e.getCause))
val replyBuilder = RemoteReply.newBuilder
.setId(request.getId)
.setException(e.getCause.getClass.getName + "$" + e.getCause.getMessage)
.setException(e.getCause.toString)
.setIsSuccessful(false)
.setIsActor(false)
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
channel.write(replyBuilder.build)
case e: Throwable =>
log.error("Could not invoke remote active object [%s :: %s] due to: %s", request.method, request.target, e)
log.error("Could not invoke remote active object [%s :: %s] due to: %s", request.getMethod, request.getTarget, e)
e.printStackTrace
channel.write(request.newReplyWithException(e))
val replyBuilder = RemoteReply.newBuilder
.setId(request.getId)
.setException(e.getClass.getName + "$" + e.getMessage)
.setIsSuccessful(false)
.setIsActor(false)
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
channel.write(replyBuilder.build)
}
}
@ -184,8 +200,10 @@ class AkkaServerHandler extends SimpleChannelUpstreamHandler with Logging {
val unescapedArgClasses = new Array[Class[_]](args.size)
val escapedArgs = for (i <- 0 until args.size) {
if (args(i).isInstanceOf[ProxyWrapper]) {
val proxyName = args(i).asInstanceOf[ProxyWrapper].proxyName
val arg = args(i)
if (arg.isInstanceOf[String] && arg.asInstanceOf[String] == "$$ProxiedByAW") {
val argString = arg.asInstanceOf[String]
val proxyName = argString.substring(argString.indexOf("$$ProxiedByAW"), argString.length)
val activeObject = createActiveObject(proxyName, timeout)
unescapedArgs(i) = activeObject
unescapedArgClasses(i) = Class.forName(proxyName)

View file

@ -8,12 +8,12 @@ import java.util.concurrent.atomic.AtomicLong
import kernel.stm.Transaction
import kernel.util.HashCode
// FIXME: will not work - can clash with other host's requests - need te prepend with hostname
object RemoteRequestIdFactory {
private val id = new AtomicLong
def nextId = id.getAndIncrement
}
@serializable class ProxyWrapper(val proxyName: String)
/*
@serializable class RemoteRequest(val message: AnyRef,
val method: String,

View file

@ -40,7 +40,33 @@ object JavaSerializationSerializer extends Serializer {
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
object JSONSerializer extends Serializer {
object JavaJSONSerializer extends Serializer {
import org.codehaus.jackson.map.ObjectMapper
private val json = new ObjectMapper
def out(obj: AnyRef): Array[Byte] = {
if (!json.canSerialize(obj.getClass)) throw new IllegalArgumentException("Can not serialize [" + obj + "] to JSON, please provide a JSON serializable object.")
val bos = new ByteArrayOutputStream
val out = new ObjectOutputStream(bos)
json.writeValue(out, obj)
out.close
bos.toByteArray
}
def in(bytes: Array[Byte], clazz: Option[Class[_]]): AnyRef = {
if (!clazz.isDefined) throw new IllegalArgumentException("Can't deserialize JSON to instance if no class is provided")
val in = new ObjectInputStream(new ByteArrayInputStream(bytes))
val obj = json.readValue(in, clazz.get).asInstanceOf[AnyRef]
in.close
obj
}
}
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
object ScalaJSONSerializer extends Serializer {
import org.codehaus.jackson.map.ObjectMapper
private val json = new ObjectMapper

View file

@ -9,7 +9,7 @@ import annotation.oneway
import kernel.config.ScalaConfig._
import com.google.inject.{AbstractModule, Scopes}
import com.jteigen.scalatest.JUnit4Runner
//import com.jteigen.scalatest.JUnit4Runner
import org.apache.camel.component.bean.ProxyHelper
import org.junit.runner.RunWith
@ -32,7 +32,7 @@ import org.apache.camel.impl.DefaultCamelContext
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
@RunWith(classOf[JUnit4Runner])
//@RunWith(classOf[JUnit4Runner])
class CamelSpec extends Spec with ShouldMatchers {
describe("A Camel routing scheme") {

View file

@ -19,7 +19,7 @@ import com.google.inject.{AbstractModule, Scopes}
import org.scalatest.Spec
import org.scalatest.matchers.ShouldMatchers
import com.jteigen.scalatest.JUnit4Runner
//simport com.jteigen.scalatest.JUnit4Runner
import org.junit.runner.RunWith
import org.junit.Test
import org.junit.Assert._
@ -27,7 +27,7 @@ import org.junit.Assert._
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
@RunWith(classOf[JUnit4Runner])
//@RunWith(classOf[JUnit4Runner])
class JerseySpec extends Spec with ShouldMatchers {
describe("A Jersey REST service") {

View file

@ -40,7 +40,7 @@ class RemoteActorSpec extends TestCase {
@Test
def testSendOneWay = {
implicit val timeout = 5000L
implicit val timeout = 500000000L
val actor = new RemoteActorSpecActorUnidirectional
actor.makeRemote(RemoteServer.HOSTNAME, RemoteServer.PORT)
actor.start
@ -52,7 +52,7 @@ class RemoteActorSpec extends TestCase {
@Test
def testSendReplySync = {
implicit val timeout = 5000L
implicit val timeout = 500000000L
val actor = new RemoteActorSpecActorBidirectional
actor.makeRemote(RemoteServer.HOSTNAME, RemoteServer.PORT)
actor.start
@ -63,7 +63,7 @@ class RemoteActorSpec extends TestCase {
@Test
def testSendReplyAsync = {
implicit val timeout = 5000L
implicit val timeout = 500000000L
val actor = new RemoteActorSpecActorBidirectional
actor.makeRemote(RemoteServer.HOSTNAME, RemoteServer.PORT)
actor.start
@ -74,7 +74,7 @@ class RemoteActorSpec extends TestCase {
@Test
def testSendReceiveException = {
implicit val timeout = 5000L
implicit val timeout = 500000000L
val actor = new RemoteActorSpecActorBidirectional
actor.makeRemote(RemoteServer.HOSTNAME, RemoteServer.PORT)
actor.start

View file

@ -8,7 +8,7 @@ import kernel.nio.{RemoteClient, RemoteServer}
import kernel.actor.{Supervisor, SupervisorFactory, Actor, StartSupervisor}
import kernel.config.ScalaConfig._
import com.jteigen.scalatest.JUnit4Runner
//import com.jteigen.scalatest.JUnit4Runner
import org.junit.runner.RunWith
import org.scalatest.Suite
@ -19,7 +19,7 @@ object Log {
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
@RunWith(classOf[JUnit4Runner])
//@RunWith(classOf[JUnit4Runner])
class RemoteSupervisorSpec extends junit.framework.TestCase with Suite {
Kernel.config

View file

@ -7,14 +7,14 @@ package se.scalablesolutions.akka.kernel
import kernel.actor.{Supervisor, SupervisorFactory, Actor, StartSupervisor}
import kernel.config.ScalaConfig._
import com.jteigen.scalatest.JUnit4Runner
//import com.jteigen.scalatest.JUnit4Runner
import org.junit.runner.RunWith
import org.scalatest.Suite
/**
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
@RunWith(classOf[JUnit4Runner])
//@RunWith(classOf[JUnit4Runner])
class SupervisorSpec extends junit.framework.TestCase with Suite {
var messageLog: String = ""