pekko/akka-samples-chat/src/main/scala/ChatServer.scala

160 lines
4.5 KiB
Scala
Raw Normal View History

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}
/******************************************************************************
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)
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:45:24 +01:00
override def shutdown = {
sessions.foreach { case (_, session) =>
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
}
}