Merge branch 'master' of git@github.com:jboner/akka

This commit is contained in:
Jonas Bonér 2010-11-23 08:19:04 +01:00
commit a7ef1da733
10 changed files with 597 additions and 82 deletions

View file

@ -286,14 +286,18 @@ object ActorRegistry extends ListenerManagement {
private[akka] def actors(address: Address) = actorsFor(address).actors private[akka] def actors(address: Address) = actorsFor(address).actors
private[akka] def actorsByUuid(address: Address) = actorsFor(address).actorsByUuid private[akka] def actorsByUuid(address: Address) = actorsFor(address).actorsByUuid
private[akka] def actorsFactories(address: Address) = actorsFor(address).actorsFactories
private[akka] def typedActors(address: Address) = actorsFor(address).typedActors private[akka] def typedActors(address: Address) = actorsFor(address).typedActors
private[akka] def typedActorsByUuid(address: Address) = actorsFor(address).typedActorsByUuid private[akka] def typedActorsByUuid(address: Address) = actorsFor(address).typedActorsByUuid
private[akka] def typedActorsFactories(address: Address) = actorsFor(address).typedActorsFactories
private[akka] class RemoteActorSet { private[akka] class RemoteActorSet {
private[ActorRegistry] val actors = new ConcurrentHashMap[String, ActorRef] private[ActorRegistry] val actors = new ConcurrentHashMap[String, ActorRef]
private[ActorRegistry] val actorsByUuid = new ConcurrentHashMap[String, ActorRef] private[ActorRegistry] val actorsByUuid = new ConcurrentHashMap[String, ActorRef]
private[ActorRegistry] val actorsFactories = new ConcurrentHashMap[String, () => ActorRef]
private[ActorRegistry] val typedActors = new ConcurrentHashMap[String, AnyRef] private[ActorRegistry] val typedActors = new ConcurrentHashMap[String, AnyRef]
private[ActorRegistry] val typedActorsByUuid = new ConcurrentHashMap[String, AnyRef] private[ActorRegistry] val typedActorsByUuid = new ConcurrentHashMap[String, AnyRef]
private[ActorRegistry] val typedActorsFactories = new ConcurrentHashMap[String, () => AnyRef]
} }
} }

View file

@ -417,6 +417,7 @@ class RemoteClientHandler(
if (result.isInstanceOf[RemoteMessageProtocol]) { if (result.isInstanceOf[RemoteMessageProtocol]) {
val reply = result.asInstanceOf[RemoteMessageProtocol] val reply = result.asInstanceOf[RemoteMessageProtocol]
val replyUuid = uuidFrom(reply.getUuid.getHigh, reply.getUuid.getLow) val replyUuid = uuidFrom(reply.getUuid.getHigh, reply.getUuid.getLow)
// log.debug("Remote client received RemoteMessageProtocol[\n%s]".format(request.toString))
log.debug("Remote client received RemoteMessageProtocol[\n%s]", reply.toString) log.debug("Remote client received RemoteMessageProtocol[\n%s]", reply.toString)
val future = futures.get(replyUuid).asInstanceOf[CompletableFuture[Any]] val future = futures.get(replyUuid).asInstanceOf[CompletableFuture[Any]]
if (reply.hasMessage) { if (reply.hasMessage) {

View file

@ -286,6 +286,21 @@ class RemoteServer extends Logging with ListenerManagement {
else registerTypedActor(id, typedActor, typedActors) else registerTypedActor(id, typedActor, typedActors)
} }
/**
* Register typed actor by interface name.
*/
def registerTypedPerSessionActor(intfClass: Class[_], factory: => AnyRef) : Unit = registerTypedActor(intfClass.getName, factory)
/**
* Register remote typed actor by a specific id.
* @param id custom actor id
* @param typedActor typed actor to register
*/
def registerTypedPerSessionActor(id: String, factory: => AnyRef): Unit = synchronized {
log.debug("Registering server side typed remote session actor with id [%s]", id)
registerTypedPerSessionActor(id, () => factory, typedActorsFactories)
}
/** /**
* Register Remote Actor by the Actor's 'id' field. It starts the Actor if it is not started already. * Register Remote Actor by the Actor's 'id' field. It starts the Actor if it is not started already.
*/ */
@ -302,15 +317,36 @@ class RemoteServer extends Logging with ListenerManagement {
else register(id, actorRef, actors) else register(id, actorRef, actors)
} }
/**
* Register Remote Session Actor by a specific 'id' passed as argument.
* <p/>
* NOTE: If you use this method to register your remote actor then you must unregister the actor by this ID yourself.
*/
def registerPerSession(id: String, factory: => ActorRef): Unit = synchronized {
log.debug("Registering server side remote session actor with id [%s]", id)
registerPerSession(id, () => factory, actorsFactories)
}
private def register[Key](id: Key, actorRef: ActorRef, registry: ConcurrentHashMap[Key, ActorRef]) { private def register[Key](id: Key, actorRef: ActorRef, registry: ConcurrentHashMap[Key, ActorRef]) {
if (_isRunning && !registry.contains(id)) { if (_isRunning) {
registry.put(id, actorRef) //TODO change to putIfAbsent
if (!actorRef.isRunning) actorRef.start if (!actorRef.isRunning) actorRef.start
registry.put(id, actorRef)
} }
} }
private def registerPerSession[Key](id: Key, factory: () => ActorRef, registry: ConcurrentHashMap[Key,() => ActorRef]) {
if (_isRunning)
registry.put(id, factory) //TODO change to putIfAbsent
}
private def registerTypedActor[Key](id: Key, typedActor: AnyRef, registry: ConcurrentHashMap[Key, AnyRef]) { private def registerTypedActor[Key](id: Key, typedActor: AnyRef, registry: ConcurrentHashMap[Key, AnyRef]) {
if (_isRunning && !registry.contains(id)) registry.put(id, typedActor) if (_isRunning)
registry.put(id, typedActor) //TODO change to putIfAbsent
}
private def registerTypedPerSessionActor[Key](id: Key, factory: () => AnyRef, registry: ConcurrentHashMap[Key,() => AnyRef]) {
if (_isRunning)
registry.put(id, factory) //TODO change to putIfAbsent
} }
/** /**
@ -341,6 +377,18 @@ class RemoteServer extends Logging with ListenerManagement {
} }
} }
/**
* Unregister Remote Actor by specific 'id'.
* <p/>
* NOTE: You need to call this method if you have registered an actor by a custom ID.
*/
def unregisterPerSession(id: String):Unit = {
if (_isRunning) {
log.info("Unregistering server side remote session actor with id [%s]", id)
actorsFactories.remove(id)
}
}
/** /**
* Unregister Remote Typed Actor by specific 'id'. * Unregister Remote Typed Actor by specific 'id'.
* <p/> * <p/>
@ -354,14 +402,28 @@ class RemoteServer extends Logging with ListenerManagement {
} }
} }
/**
* Unregister Remote Typed Actor by specific 'id'.
* <p/>
* NOTE: You need to call this method if you have registered an actor by a custom ID.
*/
def unregisterTypedPerSessionActor(id: String):Unit = {
if (_isRunning) {
typedActorsFactories.remove(id)
}
}
protected override def manageLifeCycleOfListeners = false protected override def manageLifeCycleOfListeners = false
protected[akka] override def notifyListeners(message: => Any): Unit = super.notifyListeners(message) protected[akka] override def notifyListeners(message: => Any): Unit = super.notifyListeners(message)
private[akka] def actors = ActorRegistry.actors(address) private[akka] def actors = ActorRegistry.actors(address)
private[akka] def actorsByUuid = ActorRegistry.actorsByUuid(address) private[akka] def actorsByUuid = ActorRegistry.actorsByUuid(address)
private[akka] def actorsFactories = ActorRegistry.actorsFactories(address)
private[akka] def typedActors = ActorRegistry.typedActors(address) private[akka] def typedActors = ActorRegistry.typedActors(address)
private[akka] def typedActorsByUuid = ActorRegistry.typedActorsByUuid(address) private[akka] def typedActorsByUuid = ActorRegistry.typedActorsByUuid(address)
private[akka] def typedActorsFactories = ActorRegistry.typedActorsFactories(address)
} }
object RemoteServerSslContext { object RemoteServerSslContext {
@ -429,6 +491,9 @@ class RemoteServerHandler(
val AW_PROXY_PREFIX = "$$ProxiedByAW".intern val AW_PROXY_PREFIX = "$$ProxiedByAW".intern
val CHANNEL_INIT = "channel-init".intern val CHANNEL_INIT = "channel-init".intern
val sessionActors = new ChannelLocal[ConcurrentHashMap[String, ActorRef]]()
val typedSessionActors = new ChannelLocal[ConcurrentHashMap[String, AnyRef]]()
applicationLoader.foreach(MessageSerializer.setClassLoader(_)) applicationLoader.foreach(MessageSerializer.setClassLoader(_))
/** /**
@ -439,6 +504,8 @@ class RemoteServerHandler(
override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = { override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
val clientAddress = getClientAddress(ctx) val clientAddress = getClientAddress(ctx)
sessionActors.set(event.getChannel(), new ConcurrentHashMap[String, ActorRef]())
typedSessionActors.set(event.getChannel(), new ConcurrentHashMap[String, AnyRef]())
log.debug("Remote client [%s] connected to [%s]", clientAddress, server.name) log.debug("Remote client [%s] connected to [%s]", clientAddress, server.name)
if (RemoteServer.SECURE) { if (RemoteServer.SECURE) {
val sslHandler: SslHandler = ctx.getPipeline.get(classOf[SslHandler]) val sslHandler: SslHandler = ctx.getPipeline.get(classOf[SslHandler])
@ -458,6 +525,22 @@ class RemoteServerHandler(
override def channelDisconnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = { override def channelDisconnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
val clientAddress = getClientAddress(ctx) val clientAddress = getClientAddress(ctx)
log.debug("Remote client [%s] disconnected from [%s]", clientAddress, server.name) log.debug("Remote client [%s] disconnected from [%s]", clientAddress, server.name)
// stop all session actors
val channelActors = sessionActors.remove(event.getChannel)
if (channelActors ne null) {
val channelActorsIterator = channelActors.elements
while (channelActorsIterator.hasMoreElements) {
channelActorsIterator.nextElement.stop
}
}
val channelTypedActors = typedSessionActors.remove(event.getChannel)
if (channelTypedActors ne null) {
val channelTypedActorsIterator = channelTypedActors.elements
while (channelTypedActorsIterator.hasMoreElements) {
TypedActor.stop(channelTypedActorsIterator.nextElement)
}
}
server.notifyListeners(RemoteServerClientDisconnected(server, clientAddress)) server.notifyListeners(RemoteServerClientDisconnected(server, clientAddress))
} }
@ -512,7 +595,7 @@ class RemoteServerHandler(
val actorRef = val actorRef =
try { try {
createActor(actorInfo).start createActor(actorInfo, channel).start
} catch { } catch {
case e: SecurityException => case e: SecurityException =>
channel.write(createErrorReplyMessage(e, request, AkkaActorType.ScalaActor)) channel.write(createErrorReplyMessage(e, request, AkkaActorType.ScalaActor))
@ -544,7 +627,7 @@ class RemoteServerHandler(
val exception = f.exception val exception = f.exception
if (exception.isDefined) { if (exception.isDefined) {
log.debug("Returning exception from actor invocation [%s]".format(exception.get)) log.debug("Returning exception from actor invocation [%s]",exception.get)
try { try {
channel.write(createErrorReplyMessage(exception.get, request, AkkaActorType.ScalaActor)) channel.write(createErrorReplyMessage(exception.get, request, AkkaActorType.ScalaActor))
} catch { } catch {
@ -586,7 +669,7 @@ class RemoteServerHandler(
val typedActorInfo = actorInfo.getTypedActorInfo val typedActorInfo = actorInfo.getTypedActorInfo
log.debug("Dispatching to remote typed actor [%s :: %s]", typedActorInfo.getMethod, typedActorInfo.getInterface) log.debug("Dispatching to remote typed actor [%s :: %s]", typedActorInfo.getMethod, typedActorInfo.getInterface)
val typedActor = createTypedActor(actorInfo) val typedActor = createTypedActor(actorInfo, channel)
val args = MessageSerializer.deserialize(request.getMessage).asInstanceOf[Array[AnyRef]].toList val args = MessageSerializer.deserialize(request.getMessage).asInstanceOf[Array[AnyRef]].toList
val argClasses = args.map(_.getClass) val argClasses = args.map(_.getClass)
@ -594,19 +677,15 @@ class RemoteServerHandler(
val messageReceiver = typedActor.getClass.getDeclaredMethod(typedActorInfo.getMethod, argClasses: _*) val messageReceiver = typedActor.getClass.getDeclaredMethod(typedActorInfo.getMethod, argClasses: _*)
if (request.getOneWay) messageReceiver.invoke(typedActor, args: _*) if (request.getOneWay) messageReceiver.invoke(typedActor, args: _*)
else { else {
val result = messageReceiver.invoke(typedActor, args: _*) match { //Sends the response
case f: Future[_] => f.await.result.get //TODO replace this with a Listener on the Future to avoid blocking def sendResponse(result: Either[Any,Throwable]): Unit = try {
case other => other
}
log.debug("Returning result from remote typed actor invocation [%s]", result)
val messageBuilder = RemoteActorSerialization.createRemoteMessageProtocolBuilder( val messageBuilder = RemoteActorSerialization.createRemoteMessageProtocolBuilder(
None, None,
Right(request.getUuid), Right(request.getUuid),
actorInfo.getId, actorInfo.getId,
actorInfo.getTarget, actorInfo.getTarget,
actorInfo.getTimeout, actorInfo.getTimeout,
Left(result), result,
true, true,
None, None,
None, None,
@ -614,6 +693,19 @@ class RemoteServerHandler(
None) None)
if (request.hasSupervisorUuid) messageBuilder.setSupervisorUuid(request.getSupervisorUuid) if (request.hasSupervisorUuid) messageBuilder.setSupervisorUuid(request.getSupervisorUuid)
channel.write(messageBuilder.build) channel.write(messageBuilder.build)
log.debug("Returning result from remote typed actor invocation [%s]", result)
} catch {
case e: Throwable => server.notifyListeners(RemoteServerError(e, server))
}
messageReceiver.invoke(typedActor, args: _*) match {
case f: Future[_] => //If it's a future, we can lift on that to defer the send to when the future is completed
f.onComplete( future => {
val result: Either[Any,Throwable] = if (future.exception.isDefined) Right(future.exception.get) else Left(future.result.get)
sendResponse(result)
})
case other => sendResponse(Left(other))
}
} }
} catch { } catch {
case e: InvocationTargetException => case e: InvocationTargetException =>
@ -633,10 +725,26 @@ class RemoteServerHandler(
server.actorsByUuid.get(uuid) server.actorsByUuid.get(uuid)
} }
private def findActorFactory(id: String) : () => ActorRef = {
server.actorsFactories.get(id)
}
private def findSessionActor(id: String, channel: Channel) : ActorRef = {
sessionActors.get(channel).get(id)
}
private def findTypedActorById(id: String) : AnyRef = { private def findTypedActorById(id: String) : AnyRef = {
server.typedActors.get(id) server.typedActors.get(id)
} }
private def findTypedActorFactory(id: String) : () => AnyRef = {
server.typedActorsFactories.get(id)
}
private def findTypedSessionActor(id: String, channel: Channel) : AnyRef = {
typedSessionActors.get(channel).get(id)
}
private def findTypedActorByUuid(uuid: String) : AnyRef = { private def findTypedActorByUuid(uuid: String) : AnyRef = {
server.typedActorsByUuid.get(uuid) server.typedActorsByUuid.get(uuid)
} }
@ -656,22 +764,36 @@ class RemoteServerHandler(
} }
/** /**
* Creates a new instance of the actor with name, uuid and timeout specified as arguments. * gets the actor from the session, or creates one if there is a factory for it
*
* If actor already created then just return it from the registry.
*
* Does not start the actor.
*/ */
private def createActor(actorInfo: ActorInfoProtocol): ActorRef = { private def createSessionActor(actorInfo: ActorInfoProtocol, channel: Channel): ActorRef = {
val uuid = actorInfo.getUuid val uuid = actorInfo.getUuid
val id = actorInfo.getId val id = actorInfo.getId
val sessionActorRefOrNull = findSessionActor(id, channel)
if (sessionActorRefOrNull ne null)
sessionActorRefOrNull
else
{
// we dont have it in the session either, see if we have a factory for it
val actorFactoryOrNull = findActorFactory(id)
if (actorFactoryOrNull ne null) {
val actorRef = actorFactoryOrNull()
actorRef.uuid = uuidFrom(uuid.getHigh,uuid.getLow)
sessionActors.get(channel).put(id, actorRef)
actorRef
}
else
null
}
}
val name = actorInfo.getTarget
private def createClientManagedActor(actorInfo: ActorInfoProtocol): ActorRef = {
val uuid = actorInfo.getUuid
val id = actorInfo.getId
val timeout = actorInfo.getTimeout val timeout = actorInfo.getTimeout
val name = actorInfo.getTarget
val actorRefOrNull = findActorByIdOrUuid(id, uuidFrom(uuid.getHigh,uuid.getLow).toString)
if (actorRefOrNull eq null) {
try { try {
if (RemoteServer.UNTRUSTED_MODE) throw new SecurityException( if (RemoteServer.UNTRUSTED_MODE) throw new SecurityException(
"Remote server is operating is untrusted mode, can not create remote actors on behalf of the remote client") "Remote server is operating is untrusted mode, can not create remote actors on behalf of the remote client")
@ -692,19 +814,61 @@ class RemoteServerHandler(
server.notifyListeners(RemoteServerError(e, server)) server.notifyListeners(RemoteServerError(e, server))
throw e throw e
} }
} else actorRefOrNull
} }
private def createTypedActor(actorInfo: ActorInfoProtocol): AnyRef = { /**
* Creates a new instance of the actor with name, uuid and timeout specified as arguments.
*
* If actor already created then just return it from the registry.
*
* Does not start the actor.
*/
private def createActor(actorInfo: ActorInfoProtocol, channel: Channel): ActorRef = {
val uuid = actorInfo.getUuid val uuid = actorInfo.getUuid
val id = actorInfo.getId val id = actorInfo.getId
val typedActorOrNull = findTypedActorByIdOrUuid(id, uuidFrom(uuid.getHigh,uuid.getLow).toString) val actorRefOrNull = findActorByIdOrUuid(id, uuidFrom(uuid.getHigh,uuid.getLow).toString)
if (typedActorOrNull eq null) { if (actorRefOrNull ne null)
actorRefOrNull
else
{
// the actor has not been registered globally. See if we have it in the session
val sessionActorRefOrNull = createSessionActor(actorInfo, channel)
if (sessionActorRefOrNull ne null)
sessionActorRefOrNull
else // maybe it is a client managed actor
createClientManagedActor(actorInfo)
}
}
/**
* gets the actor from the session, or creates one if there is a factory for it
*/
private def createTypedSessionActor(actorInfo: ActorInfoProtocol, channel: Channel):AnyRef ={
val id = actorInfo.getId
val sessionActorRefOrNull = findTypedSessionActor(id, channel)
if (sessionActorRefOrNull ne null)
sessionActorRefOrNull
else {
val actorFactoryOrNull = findTypedActorFactory(id)
if (actorFactoryOrNull ne null) {
val newInstance = actorFactoryOrNull()
typedSessionActors.get(channel).put(id, newInstance)
newInstance
}
else
null
}
}
private def createClientManagedTypedActor(actorInfo: ActorInfoProtocol) = {
val typedActorInfo = actorInfo.getTypedActorInfo val typedActorInfo = actorInfo.getTypedActorInfo
val interfaceClassname = typedActorInfo.getInterface val interfaceClassname = typedActorInfo.getInterface
val targetClassname = actorInfo.getTarget val targetClassname = actorInfo.getTarget
val uuid = actorInfo.getUuid
try { try {
if (RemoteServer.UNTRUSTED_MODE) throw new SecurityException( if (RemoteServer.UNTRUSTED_MODE) throw new SecurityException(
@ -727,7 +891,24 @@ class RemoteServerHandler(
server.notifyListeners(RemoteServerError(e, server)) server.notifyListeners(RemoteServerError(e, server))
throw e throw e
} }
} else typedActorOrNull }
private def createTypedActor(actorInfo: ActorInfoProtocol, channel: Channel): AnyRef = {
val uuid = actorInfo.getUuid
val id = actorInfo.getId
val typedActorOrNull = findTypedActorByIdOrUuid(id, uuidFrom(uuid.getHigh,uuid.getLow).toString)
if (typedActorOrNull ne null)
typedActorOrNull
else
{
// the actor has not been registered globally. See if we have it in the session
val sessionActorRefOrNull = createTypedSessionActor(actorInfo, channel)
if (sessionActorRefOrNull ne null)
sessionActorRefOrNull
else // maybe it is a client managed actor
createClientManagedTypedActor(actorInfo)
}
} }
private def createErrorReplyMessage(exception: Throwable, request: RemoteMessageProtocol, actorType: AkkaActorType): RemoteMessageProtocol = { private def createErrorReplyMessage(exception: Throwable, request: RemoteMessageProtocol, actorType: AkkaActorType): RemoteMessageProtocol = {

View file

@ -0,0 +1,8 @@
package akka.actor;
public interface RemoteTypedSessionActor {
public void login(String user);
public String getUser();
public void doSomethingFunny() throws Exception;
}

View file

@ -0,0 +1,49 @@
package akka.actor.remote;
import akka.actor.*;
import java.util.Set;
import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
public class RemoteTypedSessionActorImpl extends TypedActor implements RemoteTypedSessionActor {
private static Set<RemoteTypedSessionActor> instantiatedSessionActors = new HashSet<RemoteTypedSessionActor>();
public static Set<RemoteTypedSessionActor> getInstances() {
return instantiatedSessionActors;
}
@Override
public void preStart() {
instantiatedSessionActors.add(this);
}
@Override
public void postStop() {
instantiatedSessionActors.remove(this);
}
private String user="anonymous";
@Override
public void login(String user) {
this.user = user;
}
@Override
public String getUser()
{
return this.user;
}
@Override
public void doSomethingFunny() throws Exception
{
throw new Exception("Bad boy");
}
}

View file

@ -3,6 +3,7 @@ package akka.actor.remote
import java.util.concurrent.{CountDownLatch, TimeUnit} import java.util.concurrent.{CountDownLatch, TimeUnit}
import org.scalatest.junit.JUnitSuite import org.scalatest.junit.JUnitSuite
import org.junit.{Test, Before, After} import org.junit.{Test, Before, After}
import akka.util._
import akka.remote.{RemoteServer, RemoteClient} import akka.remote.{RemoteServer, RemoteClient}
import akka.actor.Actor._ import akka.actor.Actor._

View file

@ -0,0 +1,162 @@
/**
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
*/
package akka.actor.remote
import org.scalatest._
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.BeforeAndAfterAll
import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
import java.util.concurrent.TimeUnit
import akka.remote.{RemoteServer, RemoteClient}
import akka.actor._
import akka.actor.Actor._
import RemoteTypedActorLog._
object ServerInitiatedRemoteSessionActorSpec {
val HOSTNAME = "localhost"
val PORT = 9990
var server: RemoteServer = null
case class Login(user:String)
case class GetUser()
case class DoSomethingFunny()
var instantiatedSessionActors= Set[ActorRef]()
class RemoteStatefullSessionActorSpec extends Actor {
var user : String= "anonymous"
override def preStart = {
instantiatedSessionActors += self
}
override def postStop = {
instantiatedSessionActors -= self
}
def receive = {
case Login(user) =>
this.user = user
case GetUser() =>
self.reply(this.user)
case DoSomethingFunny() =>
throw new Exception("Bad boy")
}
}
}
@RunWith(classOf[JUnitRunner])
class ServerInitiatedRemoteSessionActorSpec extends
FlatSpec with
ShouldMatchers with
BeforeAndAfterEach {
import ServerInitiatedRemoteSessionActorSpec._
private val unit = TimeUnit.MILLISECONDS
override def beforeEach = {
server = new RemoteServer()
server.start(HOSTNAME, PORT)
server.registerPerSession("untyped-session-actor-service", actorOf[RemoteStatefullSessionActorSpec])
Thread.sleep(1000)
}
// make sure the servers shutdown cleanly after the test has finished
override def afterEach = {
try {
server.shutdown
RemoteClient.shutdownAll
Thread.sleep(1000)
} catch {
case e => ()
}
}
"A remote session Actor" should "create a new session actor per connection" in {
clearMessageLogs
val session1 = RemoteClient.actorFor(
"untyped-session-actor-service",
5000L,
HOSTNAME, PORT)
val default1 = session1 !! GetUser()
default1.get.asInstanceOf[String] should equal ("anonymous")
session1 ! Login("session[1]")
val result1 = session1 !! GetUser()
result1.get.asInstanceOf[String] should equal ("session[1]")
session1.stop()
RemoteClient.shutdownAll
//RemoteClient.clientFor(HOSTNAME, PORT).connect
val session2 = RemoteClient.actorFor(
"untyped-session-actor-service",
5000L,
HOSTNAME, PORT)
// since this is a new session, the server should reset the state
val default2 = session2 !! GetUser()
default2.get.asInstanceOf[String] should equal ("anonymous")
session2.stop()
}
it should "stop the actor when the client disconnects" in {
val session1 = RemoteClient.actorFor(
"untyped-session-actor-service",
5000L,
HOSTNAME, PORT)
val default1 = session1 !! GetUser()
default1.get.asInstanceOf[String] should equal ("anonymous")
instantiatedSessionActors should have size (1)
RemoteClient.shutdownAll
Thread.sleep(1000)
instantiatedSessionActors should have size (0)
}
it should "stop the actor when there is an error" in {
val session1 = RemoteClient.actorFor(
"untyped-session-actor-service",
5000L,
HOSTNAME, PORT)
session1 ! DoSomethingFunny()
session1.stop()
Thread.sleep(1000)
instantiatedSessionActors should have size (0)
}
it should "be able to unregister" in {
server.registerPerSession("my-service-1", actorOf[RemoteStatefullSessionActorSpec])
server.actorsFactories.get("my-service-1") should not be (null)
server.unregisterPerSession("my-service-1")
server.actorsFactories.get("my-service-1") should be (null)
}
}

View file

@ -0,0 +1,108 @@
/**
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
*/
package akka.actor.remote
import org.scalatest._
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.BeforeAndAfterAll
import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
import java.util.concurrent.TimeUnit
import akka.remote.{RemoteServer, RemoteClient}
import akka.actor._
import RemoteTypedActorLog._
object ServerInitiatedRemoteTypedSessionActorSpec {
val HOSTNAME = "localhost"
val PORT = 9990
var server: RemoteServer = null
}
@RunWith(classOf[JUnitRunner])
class ServerInitiatedRemoteTypedSessionActorSpec extends
FlatSpec with
ShouldMatchers with
BeforeAndAfterEach {
import ServerInitiatedRemoteTypedActorSpec._
private val unit = TimeUnit.MILLISECONDS
override def beforeEach = {
server = new RemoteServer()
server.start(HOSTNAME, PORT)
server.registerTypedPerSessionActor("typed-session-actor-service",
TypedActor.newInstance(classOf[RemoteTypedSessionActor], classOf[RemoteTypedSessionActorImpl], 1000))
Thread.sleep(1000)
}
// make sure the servers shutdown cleanly after the test has finished
override def afterEach = {
try {
server.shutdown
RemoteClient.shutdownAll
Thread.sleep(1000)
} catch {
case e => ()
}
}
"A remote session Actor" should "create a new session actor per connection" in {
clearMessageLogs
val session1 = RemoteClient.typedActorFor(classOf[RemoteTypedSessionActor], "typed-session-actor-service", 5000L, HOSTNAME, PORT)
session1.getUser() should equal ("anonymous")
session1.login("session[1]")
session1.getUser() should equal ("session[1]")
RemoteClient.shutdownAll
val session2 = RemoteClient.typedActorFor(classOf[RemoteTypedSessionActor], "typed-session-actor-service", 5000L, HOSTNAME, PORT)
session2.getUser() should equal ("anonymous")
}
it should "stop the actor when the client disconnects" in {
val session1 = RemoteClient.typedActorFor(classOf[RemoteTypedSessionActor], "typed-session-actor-service", 5000L, HOSTNAME, PORT)
session1.getUser() should equal ("anonymous")
RemoteTypedSessionActorImpl.getInstances() should have size (1)
RemoteClient.shutdownAll
Thread.sleep(1000)
RemoteTypedSessionActorImpl.getInstances() should have size (0)
}
it should "stop the actor when there is an error" in {
val session1 = RemoteClient.typedActorFor(classOf[RemoteTypedSessionActor], "typed-session-actor-service", 5000L, HOSTNAME, PORT)
session1.doSomethingFunny()
RemoteClient.shutdownAll
Thread.sleep(1000)
RemoteTypedSessionActorImpl.getInstances() should have size (0)
}
it should "be able to unregister" in {
server.registerTypedPerSessionActor("my-service-1",TypedActor.newInstance(classOf[RemoteTypedSessionActor], classOf[RemoteTypedSessionActorImpl], 1000))
server.typedActorsFactories.get("my-service-1") should not be (null)
server.unregisterTypedPerSessionActor("my-service-1")
server.typedActorsFactories.get("my-service-1") should be (null)
}
}

View file

@ -7,7 +7,7 @@ import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith import org.junit.runner.RunWith
import akka.serialization.Serializer.ScalaJSON import akka.serialization.Serializer.ScalaJSON
/*
object Protocols { object Protocols {
import sjson.json.DefaultProtocol._ import sjson.json.DefaultProtocol._
case class Shop(store: String, item: String, price: Int) case class Shop(store: String, item: String, price: Int)
@ -50,3 +50,4 @@ class ScalaJSONSerializerSpec extends
} }
} }
} }
*/