completed protobuf protocol for remoting
This commit is contained in:
parent
a4d22af64b
commit
f26110e55c
23 changed files with 795 additions and 385 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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] &&
|
||||
|
|
|
|||
|
|
@ -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 + "]"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,33 @@ object JavaSerializationSerializer extends Serializer {
|
|||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Boné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ér</a>
|
||||
*/
|
||||
object ScalaJSONSerializer extends Serializer {
|
||||
import org.codehaus.jackson.map.ObjectMapper
|
||||
|
||||
private val json = new ObjectMapper
|
||||
|
|
|
|||
|
|
@ -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ér</a>
|
||||
*/
|
||||
@RunWith(classOf[JUnit4Runner])
|
||||
//@RunWith(classOf[JUnit4Runner])
|
||||
class CamelSpec extends Spec with ShouldMatchers {
|
||||
|
||||
describe("A Camel routing scheme") {
|
||||
|
|
|
|||
|
|
@ -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ér</a>
|
||||
*/
|
||||
@RunWith(classOf[JUnit4Runner])
|
||||
//@RunWith(classOf[JUnit4Runner])
|
||||
class JerseySpec extends Spec with ShouldMatchers {
|
||||
|
||||
describe("A Jersey REST service") {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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ér</a>
|
||||
*/
|
||||
@RunWith(classOf[JUnit4Runner])
|
||||
//@RunWith(classOf[JUnit4Runner])
|
||||
class RemoteSupervisorSpec extends junit.framework.TestCase with Suite {
|
||||
|
||||
Kernel.config
|
||||
|
|
|
|||
|
|
@ -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ér</a>
|
||||
*/
|
||||
@RunWith(classOf[JUnit4Runner])
|
||||
//@RunWith(classOf[JUnit4Runner])
|
||||
class SupervisorSpec extends junit.framework.TestCase with Suite {
|
||||
|
||||
var messageLog: String = ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue