2011-09-14 16:09:17 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
|
|
|
|
*/
|
|
|
|
|
package akka.remote.testconductor
|
|
|
|
|
|
2012-05-02 21:56:26 +02:00
|
|
|
import akka.actor.{ Actor, ActorRef, ActorSystem, LoggingFSM, Props }
|
2011-09-14 16:09:17 +02:00
|
|
|
import RemoteConnection.getAddrString
|
|
|
|
|
import akka.util.duration._
|
|
|
|
|
import TestConductorProtocol._
|
|
|
|
|
import org.jboss.netty.channel.{ Channel, SimpleChannelUpstreamHandler, ChannelHandlerContext, ChannelStateEvent, MessageEvent }
|
|
|
|
|
import com.eaio.uuid.UUID
|
2012-05-02 21:56:26 +02:00
|
|
|
import com.typesafe.config.ConfigFactory
|
|
|
|
|
import akka.util.Timeout
|
|
|
|
|
import akka.util.Duration
|
|
|
|
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
|
|
|
|
import akka.pattern.ask
|
|
|
|
|
import akka.dispatch.Await
|
|
|
|
|
import scala.util.control.NoStackTrace
|
|
|
|
|
import akka.actor.Status
|
|
|
|
|
import akka.event.LoggingAdapter
|
|
|
|
|
import akka.actor.PoisonPill
|
|
|
|
|
import akka.event.Logging
|
2011-09-14 16:09:17 +02:00
|
|
|
|
|
|
|
|
object Player extends BarrierSync {
|
|
|
|
|
|
2012-05-02 21:56:26 +02:00
|
|
|
val system = ActorSystem("Player", ConfigFactory.load().getConfig("player"))
|
|
|
|
|
|
|
|
|
|
object Settings {
|
|
|
|
|
val config = system.settings.config
|
|
|
|
|
|
|
|
|
|
implicit val BarrierTimeout = Timeout(Duration(config.getMilliseconds("barrier-timeout"), MILLISECONDS))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private val server = system.actorOf(Props[ClientFSM], "client")
|
2011-09-14 16:09:17 +02:00
|
|
|
|
|
|
|
|
override def enter(name: String*) {
|
2012-05-02 21:56:26 +02:00
|
|
|
system.log.debug("entering barriers " + name.mkString("(", ", ", ")"))
|
2011-09-14 16:09:17 +02:00
|
|
|
name foreach { b ⇒
|
2012-05-02 21:56:26 +02:00
|
|
|
import Settings.BarrierTimeout
|
|
|
|
|
Await.result(server ? EnterBarrier(b), Duration.Inf)
|
|
|
|
|
system.log.debug("passed barrier {}", b)
|
2011-09-14 16:09:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object ClientFSM {
|
|
|
|
|
sealed trait State
|
|
|
|
|
case object Connecting extends State
|
|
|
|
|
case object Connected extends State
|
|
|
|
|
|
2012-05-02 21:56:26 +02:00
|
|
|
case class Data(channel: Channel, msg: Either[List[ClientOp], (String, ActorRef)])
|
2011-09-14 16:09:17 +02:00
|
|
|
|
|
|
|
|
class ConnectionFailure(msg: String) extends RuntimeException(msg) with NoStackTrace
|
|
|
|
|
case object Disconnected
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ClientFSM extends Actor with LoggingFSM[ClientFSM.State, ClientFSM.Data] {
|
|
|
|
|
import ClientFSM._
|
|
|
|
|
|
2012-05-02 21:56:26 +02:00
|
|
|
val config = context.system.settings.config
|
|
|
|
|
|
|
|
|
|
val name = config.getString("akka.testconductor.name")
|
|
|
|
|
val host = config.getString("akka.testconductor.host")
|
|
|
|
|
val port = config.getInt("akka.testconductor.port")
|
|
|
|
|
val handler = new PlayerHandler(self, Logging(context.system, "PlayerHandler"))
|
2011-09-14 16:09:17 +02:00
|
|
|
|
2012-05-02 21:56:26 +02:00
|
|
|
val myself = "XXX"
|
|
|
|
|
val myport = 12345
|
2011-09-14 16:09:17 +02:00
|
|
|
|
|
|
|
|
startWith(Connecting, Data(RemoteConnection(Client, host, port, handler), Left(Nil)))
|
|
|
|
|
|
|
|
|
|
when(Connecting, stateTimeout = 10 seconds) {
|
|
|
|
|
case Event(msg: ClientOp, Data(channel, Left(msgs))) ⇒
|
|
|
|
|
stay using Data(channel, Left(msg :: msgs))
|
|
|
|
|
case Event(Connected, Data(channel, Left(msgs))) ⇒
|
2012-05-02 21:56:26 +02:00
|
|
|
val hello = Hello.newBuilder.setName(name).setHost(myself).setPort(myport).build
|
2011-09-14 16:09:17 +02:00
|
|
|
channel.write(Wrapper.newBuilder.setHello(hello).build)
|
|
|
|
|
msgs.reverse foreach sendMsg(channel)
|
|
|
|
|
goto(Connected) using Data(channel, Left(Nil))
|
|
|
|
|
case Event(_: ConnectionFailure, _) ⇒
|
|
|
|
|
// System.exit(1)
|
|
|
|
|
stop
|
|
|
|
|
case Event(StateTimeout, _) ⇒
|
2012-05-02 21:56:26 +02:00
|
|
|
log.error("connect timeout to TestConductor")
|
2011-09-14 16:09:17 +02:00
|
|
|
// System.exit(1)
|
|
|
|
|
stop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
when(Connected) {
|
|
|
|
|
case Event(Disconnected, _) ⇒
|
2012-05-02 21:56:26 +02:00
|
|
|
log.info("disconnected from TestConductor")
|
2011-09-14 16:09:17 +02:00
|
|
|
throw new ConnectionFailure("disconnect")
|
|
|
|
|
case Event(msg: EnterBarrier, Data(channel, _)) ⇒
|
|
|
|
|
sendMsg(channel)(msg)
|
2012-05-02 21:56:26 +02:00
|
|
|
stay using Data(channel, Right((msg.name, sender)))
|
2011-09-14 16:09:17 +02:00
|
|
|
case Event(msg: Wrapper, Data(channel, Right((barrier, sender)))) if msg.getAllFields.size == 1 ⇒
|
|
|
|
|
if (msg.hasBarrier) {
|
|
|
|
|
val b = msg.getBarrier.getName
|
|
|
|
|
if (b != barrier) {
|
2012-05-02 21:56:26 +02:00
|
|
|
sender ! Status.Failure(new RuntimeException("wrong barrier " + b + " received while waiting for " + barrier))
|
2011-09-14 16:09:17 +02:00
|
|
|
} else {
|
|
|
|
|
sender ! b
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stay using Data(channel, Left(Nil))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onTermination {
|
|
|
|
|
case StopEvent(_, _, Data(channel, _)) ⇒
|
|
|
|
|
channel.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def sendMsg(channel: Channel)(msg: ClientOp) {
|
|
|
|
|
msg match {
|
|
|
|
|
case EnterBarrier(name) ⇒
|
|
|
|
|
val enter = TestConductorProtocol.EnterBarrier.newBuilder.setName(name).build
|
|
|
|
|
channel.write(Wrapper.newBuilder.setBarrier(enter).build)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-02 21:56:26 +02:00
|
|
|
class PlayerHandler(fsm: ActorRef, log: LoggingAdapter) extends SimpleChannelUpstreamHandler {
|
2011-09-14 16:09:17 +02:00
|
|
|
|
|
|
|
|
import ClientFSM._
|
|
|
|
|
|
|
|
|
|
override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
|
|
|
|
|
val channel = event.getChannel
|
2012-05-02 21:56:26 +02:00
|
|
|
log.debug("connected to {}", getAddrString(channel))
|
2011-09-14 16:09:17 +02:00
|
|
|
fsm ! Connected
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def channelDisconnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
|
|
|
|
|
val channel = event.getChannel
|
2012-05-02 21:56:26 +02:00
|
|
|
log.debug("disconnected from {}", getAddrString(channel))
|
|
|
|
|
fsm ! PoisonPill
|
2011-09-14 16:09:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) = {
|
|
|
|
|
val channel = event.getChannel
|
2012-05-02 21:56:26 +02:00
|
|
|
log.debug("message from {}: {}", getAddrString(channel), event.getMessage)
|
2011-09-14 16:09:17 +02:00
|
|
|
event.getMessage match {
|
|
|
|
|
case msg: Wrapper if msg.getAllFields.size == 1 ⇒
|
|
|
|
|
fsm ! msg
|
|
|
|
|
case msg ⇒
|
2012-05-02 21:56:26 +02:00
|
|
|
log.info("server {} sent garbage '{}', disconnecting", getAddrString(channel), msg)
|
2011-09-14 16:09:17 +02:00
|
|
|
channel.close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|