2009-12-27 22:57:45 +01:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package se.scalablesolutions.akka.sample.chat
|
|
|
|
|
|
|
|
|
|
import se.scalablesolutions.akka.actor.{SupervisorFactory, Actor, RemoteActor}
|
|
|
|
|
import se.scalablesolutions.akka.remote.RemoteServer
|
|
|
|
|
import se.scalablesolutions.akka.config.ScalaConfig._
|
|
|
|
|
import se.scalablesolutions.akka.config.{OneForOneStrategy}
|
|
|
|
|
|
2009-12-28 08:25:21 +01:00
|
|
|
/******************************************************************************
|
|
|
|
|
To run the sample:
|
|
|
|
|
1. Run 'mvn install' (builds and deploys jar to AKKA_HOME/deploy)
|
|
|
|
|
2. In another shell run 'java -jar ./dist/akka-0.6.jar' to start up Akka microkernel
|
|
|
|
|
3. In the first shell run 'mvn scala:console -o'
|
|
|
|
|
4. In the REPL you get execute:
|
|
|
|
|
- scala> import se.scalablesolutions.akka.sample.chat._
|
|
|
|
|
- scala> Runner.run
|
|
|
|
|
5. See the chat simulation run
|
|
|
|
|
6. Run it again if you like
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
2009-12-27 22:57:45 +01:00
|
|
|
/**
|
2009-12-28 08:15:56 +01:00
|
|
|
* ChatServer's internal events.
|
2009-12-27 22:57:45 +01:00
|
|
|
*/
|
|
|
|
|
sealed trait Event
|
|
|
|
|
case class Login(user: String) extends Event
|
|
|
|
|
case class Logout(user: String) extends Event
|
|
|
|
|
case class GetChatLog(from: String) extends Event
|
|
|
|
|
case class ChatLog(log: List[String]) extends Event
|
|
|
|
|
case class ChatMessage(from: String, message: String) extends Event
|
|
|
|
|
|
|
|
|
|
/**
|
2009-12-28 08:15:56 +01:00
|
|
|
* Chat client.
|
2009-12-27 22:57:45 +01:00
|
|
|
*/
|
2009-12-28 08:15:56 +01:00
|
|
|
class ChatClient(val name: String) {
|
|
|
|
|
import Actor.Sender.Self
|
|
|
|
|
def login = ChatServer ! Login(name)
|
|
|
|
|
def logout = ChatServer ! Logout(name)
|
|
|
|
|
def post(message: String) = ChatServer ! ChatMessage(name, name + ": " + message)
|
2009-12-28 08:25:21 +01:00
|
|
|
def chatLog: ChatLog = (ChatServer !! GetChatLog(name)).getOrElse(throw new Exception("Couldn't get the chat log from ChatServer"))
|
2009-12-28 08:15:56 +01:00
|
|
|
}
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
/**
|
|
|
|
|
* Internal chat client session.
|
|
|
|
|
*/
|
|
|
|
|
class Session(user: String, storage: Actor) extends Actor {
|
|
|
|
|
lifeCycle = Some(LifeCycle(Permanent))
|
|
|
|
|
private val loginTime = System.currentTimeMillis
|
|
|
|
|
private var userLog: List[String] = Nil
|
|
|
|
|
|
|
|
|
|
log.info("New session for user [%s] has been created", user)
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
def receive = {
|
|
|
|
|
case msg @ ChatMessage(from, message) =>
|
|
|
|
|
userLog ::= message
|
|
|
|
|
storage ! msg
|
|
|
|
|
|
|
|
|
|
case msg @ GetChatLog(_) =>
|
|
|
|
|
storage forward msg
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Chat storage holding the chat log.
|
|
|
|
|
*/
|
|
|
|
|
class Storage extends Actor {
|
|
|
|
|
lifeCycle = Some(LifeCycle(Permanent))
|
2009-12-27 22:57:45 +01:00
|
|
|
private var chatLog: List[String] = Nil
|
|
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
log.info("Chat storage is starting up...")
|
|
|
|
|
|
|
|
|
|
def receive = {
|
|
|
|
|
case msg @ ChatMessage(from, message) =>
|
|
|
|
|
log.debug("New chat message [%s]", message)
|
|
|
|
|
chatLog ::= message
|
|
|
|
|
|
|
|
|
|
case GetChatLog(_) =>
|
|
|
|
|
reply(ChatLog(chatLog.reverse))
|
2009-12-27 22:57:45 +01:00
|
|
|
}
|
2009-12-28 08:15:56 +01:00
|
|
|
}
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
/**
|
|
|
|
|
* Chat server. Manages sessions and redirects all other messages to the Session for the client.
|
|
|
|
|
*/
|
|
|
|
|
object ChatServer extends Actor {
|
|
|
|
|
id = "ChatServer"
|
|
|
|
|
faultHandler = Some(OneForOneStrategy(5, 5000))
|
|
|
|
|
trapExit = List(classOf[Exception])
|
|
|
|
|
|
|
|
|
|
private var storage: Storage = _
|
|
|
|
|
private var sessions = Map[String, Actor]()
|
|
|
|
|
|
|
|
|
|
log.info("Chat service is starting up...")
|
|
|
|
|
|
|
|
|
|
override def init = storage = spawnLink(classOf[Storage])
|
|
|
|
|
|
|
|
|
|
def receive = sessionManagement orElse chatting
|
2009-12-27 22:57:45 +01:00
|
|
|
|
|
|
|
|
private def sessionManagement: PartialFunction[Any, Unit] = {
|
2009-12-28 08:15:56 +01:00
|
|
|
case Login(username) =>
|
|
|
|
|
log.info("User [%s] has logged in", username)
|
|
|
|
|
val session = new Session(username, storage)
|
2009-12-27 22:57:45 +01:00
|
|
|
startLink(session)
|
2009-12-28 08:15:56 +01:00
|
|
|
sessions = sessions + (username -> session)
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
case Logout(username) =>
|
|
|
|
|
log.info("User [%s] has logged out", username)
|
|
|
|
|
val session = sessions(username)
|
2009-12-27 22:57:45 +01:00
|
|
|
unlink(session)
|
|
|
|
|
session.stop
|
2009-12-28 08:15:56 +01:00
|
|
|
sessions = sessions - username
|
2009-12-27 22:57:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def chatting: PartialFunction[Any, Unit] = {
|
2009-12-28 08:15:56 +01:00
|
|
|
case msg @ ChatMessage(from, _) => sessions(from) ! msg
|
|
|
|
|
case msg @ GetChatLog(from) => sessions(from) forward msg
|
2009-12-27 22:57:45 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
override def shutdown = sessions.foreach { case (_, session) =>
|
2009-12-27 22:57:45 +01:00
|
|
|
log.info("Chat server is shutting down...")
|
|
|
|
|
unlink(session)
|
|
|
|
|
session.stop
|
2009-12-28 08:15:56 +01:00
|
|
|
unlink(storage)
|
|
|
|
|
storage.stop
|
2009-12-27 22:57:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Boot {
|
|
|
|
|
val factory = SupervisorFactory(
|
|
|
|
|
SupervisorConfig(
|
|
|
|
|
RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])),
|
|
|
|
|
Supervise(
|
|
|
|
|
ChatServer,
|
2009-12-28 08:15:56 +01:00
|
|
|
LifeCycle(Permanent))
|
2009-12-27 22:57:45 +01:00
|
|
|
:: Nil))
|
|
|
|
|
factory.newInstance.start
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
object Runner {
|
|
|
|
|
ChatServer.makeRemote("localhost", 9999)
|
|
|
|
|
ChatServer.start
|
2009-12-27 22:57:45 +01:00
|
|
|
|
|
|
|
|
def run = {
|
2009-12-28 08:15:56 +01:00
|
|
|
val client = new ChatClient("jonas")
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
client.login
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
client.post("Hi there")
|
|
|
|
|
println("CHAT LOG: " + client.chatLog.log)
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
client.post("Hi again")
|
|
|
|
|
println("CHAT LOG: " + client.chatLog.log)
|
2009-12-27 22:57:45 +01:00
|
|
|
|
2009-12-28 08:15:56 +01:00
|
|
|
client.logout
|
2009-12-27 22:57:45 +01:00
|
|
|
}
|
|
|
|
|
}
|