diff --git a/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgent.scala b/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgent.scala index aba99f1..6fe4c4f 100644 --- a/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgent.scala +++ b/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgent.scala @@ -14,6 +14,7 @@ import se.callista.loganalyzer.{AccessLog, ConfirmationMessage, LogMessage, Hand * Tips: * - Då meddelanden hanteras seriellt i en actor behöver man inte * oroa sig för att göra variabler trådsäkra, de kan alltså vara "mutable". + * - i++ fungerar inte i skala, använd 'i += 1' eller i = 'i + 1' * 3. Skicka logg-meddelandet till servern * * UPPGIFT 4: @@ -26,11 +27,11 @@ import se.callista.loganalyzer.{AccessLog, ConfirmationMessage, LogMessage, Hand * - En mutable map finns under scala.collection.mutable.Map: * Lägg till element: map += key -> value * Ta bort element: map -= key - * - För att köra en metod på alla element i en lista kan foreach användas. - * T.ex "collection.foreach { x -> println(x) }" + * - För att köra en metod på alla element i en map kan foreach användas. + * T.ex "map.foreach { case (k, v) => funktion(v) }" * */ -class LogAgent(host: String, server: ActorRef) extends Actor with ActorLogging { +class LogAgent(hostname: String, server: ActorRef) extends Actor with ActorLogging { def receive = { case None => //ersätt denna rad med en korrekt pattern matching diff --git a/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgentApplication.scala b/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgentApplication.scala index 87467a5..d7f0a90 100644 --- a/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgentApplication.scala +++ b/agent/src/main/scala/se/callista/loganalyzer/agent/LogAgentApplication.scala @@ -10,17 +10,24 @@ import akka.util.duration._ object LogAgentApplication extends App { + // ladda in konfiguration (agent/src/main/resources/application.conf) val config = ConfigFactory.load() + // sätt hostname till det hostname som anges i konfigurationen för Akka Remote val hostname = config.getString("akka.remote.netty.hostname") + // skapa och starta ett nytt actor system för agenten val system = ActorSystem("LogAgentActorSystem", config) + + // peka ut server-actorn som kör på ett annat remote actor system val server = system.actorFor("akka://LogServerActorSystem@127.0.0.1:2553/user/logServer") + // skapa och starta LogAgent-actorn val agent = system.actorOf(Props(new LogAgent(hostname, server)), "logAgent") + // skapa och starta reader-actorn som läser loggfilen + // (läsningen simuleras och en slumpad logg skapas en gång i sekunden) val reader = system.actorOf(Props(new LogReaderSimulator(agent, 25)), "logReader") - val scheduler = system.scheduler.schedule(1 seconds, 1 seconds, reader, Tick) } diff --git a/common/src/main/scala/se/callista/loganalyzer/Count.scala b/common/src/main/scala/se/callista/loganalyzer/Count.scala index bb23a90..53e5930 100644 --- a/common/src/main/scala/se/callista/loganalyzer/Count.scala +++ b/common/src/main/scala/se/callista/loganalyzer/Count.scala @@ -4,8 +4,6 @@ case class Count( status: HttpStatus, count: Int) { -// override def toString = "status: %s, count: %s" format (status, count) - def toJson = "{ \"status\":\"%s\", \"count\": %s }".format(status, count) } diff --git a/server/src/main/scala/se/callista/loganalyzer/server/LogServerApplication.scala b/server/src/main/scala/se/callista/loganalyzer/server/LogServerApplication.scala index ff9075f..d87d131 100644 --- a/server/src/main/scala/se/callista/loganalyzer/server/LogServerApplication.scala +++ b/server/src/main/scala/se/callista/loganalyzer/server/LogServerApplication.scala @@ -12,29 +12,31 @@ import java.util.Date object LogServerApplication extends App { + // ladda in konfiguration (server/src/main/resources/application.conf) val config = ConfigFactory.load() + + // peka ut index-fil lazy val indexFile = io.Source.fromInputStream(getClass.getResourceAsStream("/index.html")).mkString + // skapa och starta ett nytt actor system för servern val system = ActorSystem("LogServerActorSystem", config) - val activeWebsockets = HashMap[String, WebSocket]() - - val send = (msg: String) => activeWebsockets.foreach { - case (_, s: WebSocket) => s.send(msg) - } - - val presenter = system.actorOf(Props(new WebSocketPresenter(send)), "webSocket") + // skapa och starta en websocket-actor + val presenter = system.actorOf(Props[WebSocketPresenter], "webSocket") + // skapa och starta LogServer-actorn val logServer = system.actorOf(Props(new LogServer(presenter)), "logServer") + // peka ut "databasen" val db = UnstableDatabase + // skapa och starta en webbserver på port 8080 lazy val nettyServer = unfiltered.netty.Http(8080) .handler(unfiltered.netty.websockets.Planify({ case Path(Seg("socket" :: Nil)) => { - case Open(s) => activeWebsockets += s.channel.getId.toString->s - case Close(s) => activeWebsockets -= s.channel.getId.toString - case Error(s, e) => activeWebsockets -= s.channel.getId.toString + case Open(s) => presenter ! StartSocket(s.channel.getId.toString, s) + case Close(s) => presenter ! StopSocket(s.channel.getId.toString) + case Error(s, e) => presenter ! StopSocket(s.channel.getId.toString) case Message(s, Text(msg)) => }}).onPass(_.sendUpstream(_))) .handler(unfiltered.netty.cycle.Planify({ @@ -42,11 +44,11 @@ object LogServerApplication extends App { ResponseString(indexFile) } catch { case e => BadRequest } case Path(Seg("logs" :: Nil)) => try { - ResponseString(db.latestTwenty.mkString("\n")) + ResponseString("LATEST 20 LOGS:\n"+db.latestTwenty.mkString("\n")) } catch { case e => BadRequest } case _ => ResponseString("Couldn't handle request") })) - nettyServer.run() } + diff --git a/server/src/main/scala/se/callista/loganalyzer/server/WebSocketPresenter.scala b/server/src/main/scala/se/callista/loganalyzer/server/WebSocketPresenter.scala index aa10b73..d7b56ea 100644 --- a/server/src/main/scala/se/callista/loganalyzer/server/WebSocketPresenter.scala +++ b/server/src/main/scala/se/callista/loganalyzer/server/WebSocketPresenter.scala @@ -1,12 +1,34 @@ package se.callista.loganalyzer.server +import scala.collection.mutable.Map import akka.actor.Actor import se.callista.loganalyzer.Count +import se.callista.loganalyzer.HttpStatus +import unfiltered.netty.websockets.WebSocket -class WebSocketPresenter (send: String => Unit) extends Actor { +/** + * Skickar ut Count-objekt till aktiva websockets + */ +class WebSocketPresenter extends Actor { + val counts = Map[HttpStatus, Count]() + + val sockets = Map[String, WebSocket]() + def receive = { - case c: Count => send(c.toJson) + case c: Count => { + counts += c.status -> c + sockets.foreach{ case (_, s) => s.send(c.toJson)} + } + case StartSocket(id, s) => { + sockets += id -> s + counts.foreach{ case (_, c) => s.send(c.toJson)} + } + case StopSocket(id) => sockets -= id } -} \ No newline at end of file +} + +case class StartSocket (id: String, webSocket: WebSocket) + +case class StopSocket (id: String) \ No newline at end of file