2009-08-02 12:33:42 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009 Scalable Solutions.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-07-12 23:09:54 +02:00
|
|
|
package sample.scala
|
|
|
|
|
|
2009-09-03 11:02:21 +02:00
|
|
|
import se.scalablesolutions.akka.state.{PersistentState, TransactionalState, CassandraStorageConfig}
|
|
|
|
|
import se.scalablesolutions.akka.actor.{SupervisorFactory, Actor}
|
|
|
|
|
import se.scalablesolutions.akka.config.ScalaConfig._
|
|
|
|
|
import se.scalablesolutions.akka.util.Logging
|
2009-07-26 19:12:22 +02:00
|
|
|
|
2009-08-02 12:58:20 +02:00
|
|
|
import javax.ws.rs.core.MultivaluedMap
|
|
|
|
|
import javax.ws.rs.{GET, POST, Path, Produces, WebApplicationException, Consumes}
|
2009-07-12 23:09:54 +02:00
|
|
|
|
2009-07-27 23:25:46 +02:00
|
|
|
import org.atmosphere.core.annotation.{Broadcast, BroadcastFilter => FilterBroadcast, Suspend}
|
2009-08-02 12:58:20 +02:00
|
|
|
import org.atmosphere.util.XSSHtmlFilter
|
|
|
|
|
import org.atmosphere.cpr.BroadcastFilter
|
2009-07-26 19:12:22 +02:00
|
|
|
|
2009-07-12 23:09:54 +02:00
|
|
|
class Boot {
|
2009-08-01 17:40:16 +02:00
|
|
|
object factory extends SupervisorFactory {
|
|
|
|
|
override def getSupervisorConfig: SupervisorConfig = {
|
|
|
|
|
SupervisorConfig(
|
|
|
|
|
RestartStrategy(OneForOne, 3, 100),
|
|
|
|
|
Supervise(
|
2009-08-02 12:33:42 +02:00
|
|
|
new SimpleService,
|
|
|
|
|
LifeCycle(Permanent, 100)) ::
|
2009-08-28 17:40:06 +02:00
|
|
|
Supervise(
|
|
|
|
|
new Chat,
|
|
|
|
|
LifeCycle(Permanent, 100)) ::
|
|
|
|
|
Supervise(
|
|
|
|
|
new PersistentSimpleService,
|
|
|
|
|
LifeCycle(Permanent, 100))
|
|
|
|
|
:: Nil)
|
2009-07-12 23:09:54 +02:00
|
|
|
}
|
2009-08-01 17:40:16 +02:00
|
|
|
}
|
|
|
|
|
val supervisor = factory.newSupervisor
|
|
|
|
|
supervisor.startSupervisor
|
2009-07-12 23:09:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Try service out by invoking (multiple times):
|
|
|
|
|
* <pre>
|
|
|
|
|
* curl http://localhost:9998/scalacount
|
|
|
|
|
* </pre>
|
|
|
|
|
* Or browse to the URL from a web browser.
|
2009-07-26 19:12:22 +02:00
|
|
|
*/
|
2009-07-12 23:09:54 +02:00
|
|
|
@Path("/scalacount")
|
|
|
|
|
class SimpleService extends Actor {
|
2009-08-01 17:40:16 +02:00
|
|
|
makeTransactionRequired
|
|
|
|
|
|
2009-08-28 17:40:06 +02:00
|
|
|
case object Tick
|
|
|
|
|
private val KEY = "COUNTER";
|
|
|
|
|
private var hasStartedTicking = false;
|
2009-09-03 11:02:21 +02:00
|
|
|
private val storage = TransactionalState.newMap[String, Integer]
|
2009-08-28 17:40:06 +02:00
|
|
|
|
|
|
|
|
@GET
|
|
|
|
|
@Produces(Array("text/html"))
|
|
|
|
|
def count = (this !! Tick).getOrElse(<error>Error in counter</error>)
|
|
|
|
|
|
|
|
|
|
override def receive: PartialFunction[Any, Unit] = {
|
|
|
|
|
case Tick => if (hasStartedTicking) {
|
|
|
|
|
val counter = storage.get(KEY).get.asInstanceOf[Integer].intValue
|
|
|
|
|
storage.put(KEY, new Integer(counter + 1))
|
|
|
|
|
reply(<success>Tick:{counter + 1}</success>)
|
|
|
|
|
} else {
|
|
|
|
|
storage.put(KEY, new Integer(0))
|
|
|
|
|
hasStartedTicking = true
|
|
|
|
|
reply(<success>Tick: 0</success>)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Try service out by invoking (multiple times):
|
|
|
|
|
* <pre>
|
|
|
|
|
* curl http://localhost:9998/persistentscalacount
|
|
|
|
|
* </pre>
|
|
|
|
|
* Or browse to the URL from a web browser.
|
|
|
|
|
*/
|
|
|
|
|
@Path("/persistentscalacount")
|
|
|
|
|
class PersistentSimpleService extends Actor {
|
|
|
|
|
makeTransactionRequired
|
|
|
|
|
|
2009-08-01 17:40:16 +02:00
|
|
|
case object Tick
|
|
|
|
|
private val KEY = "COUNTER";
|
|
|
|
|
private var hasStartedTicking = false;
|
2009-09-03 11:02:21 +02:00
|
|
|
private val storage = PersistentState.newMap(CassandraStorageConfig())
|
2009-08-01 17:40:16 +02:00
|
|
|
|
|
|
|
|
@GET
|
|
|
|
|
@Produces(Array("text/html"))
|
|
|
|
|
def count = (this !! Tick).getOrElse(<error>Error in counter</error>)
|
|
|
|
|
|
|
|
|
|
override def receive: PartialFunction[Any, Unit] = {
|
|
|
|
|
case Tick => if (hasStartedTicking) {
|
|
|
|
|
val counter = storage.get(KEY).get.asInstanceOf[Integer].intValue
|
|
|
|
|
storage.put(KEY, new Integer(counter + 1))
|
2009-08-02 12:33:42 +02:00
|
|
|
reply(<success>Tick:{counter + 1}</success>)
|
2009-08-01 17:40:16 +02:00
|
|
|
} else {
|
|
|
|
|
storage.put(KEY, new Integer(0))
|
|
|
|
|
hasStartedTicking = true
|
|
|
|
|
reply(<success>Tick: 0</success>)
|
2009-07-26 19:12:22 +02:00
|
|
|
}
|
2009-08-01 17:40:16 +02:00
|
|
|
}
|
2009-07-25 21:35:47 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-26 19:12:22 +02:00
|
|
|
@Path("/chat")
|
2009-08-01 17:40:16 +02:00
|
|
|
class Chat extends Actor with Logging {
|
|
|
|
|
makeTransactionRequired
|
2009-07-26 19:12:22 +02:00
|
|
|
|
2009-08-01 17:40:16 +02:00
|
|
|
case class Chat(val who: String, val what: String, val msg: String)
|
2009-07-26 19:12:22 +02:00
|
|
|
|
2009-08-02 12:33:42 +02:00
|
|
|
@Suspend
|
|
|
|
|
@GET
|
|
|
|
|
@Produces(Array("text/html"))
|
|
|
|
|
@FilterBroadcast(Array(classOf[XSSHtmlFilter], classOf[JsonpFilter]))
|
2009-08-02 12:58:20 +02:00
|
|
|
def suspend = <!-- Comet is a programming technique that enables web servers to send data to the client without having any need for the client to request it. -->
|
2009-08-02 12:33:42 +02:00
|
|
|
|
2009-08-01 17:40:16 +02:00
|
|
|
override def receive: PartialFunction[Any, Unit] = {
|
|
|
|
|
case Chat(who, what, msg) => {
|
|
|
|
|
what match {
|
|
|
|
|
case "login" => reply("System Message__" + who + " has joined.")
|
|
|
|
|
case "post" => reply("" + who + "__" + msg)
|
|
|
|
|
case _ => throw new WebApplicationException(422)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case x => log.info("recieve unknown: " + x)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Broadcast
|
|
|
|
|
@Consumes(Array("application/x-www-form-urlencoded"))
|
|
|
|
|
@POST
|
|
|
|
|
@Produces(Array("text/html"))
|
|
|
|
|
@FilterBroadcast(Array(classOf[XSSHtmlFilter], classOf[JsonpFilter]))
|
|
|
|
|
def publishMessage(form: MultivaluedMap[String, String]) = (this !! Chat(form.getFirst("name"), form.getFirst("action"), form.getFirst("message"))).getOrElse("System__error")
|
|
|
|
|
}
|
2009-07-29 20:17:40 +02:00
|
|
|
|
|
|
|
|
|
2009-08-01 17:40:16 +02:00
|
|
|
class JsonpFilter extends BroadcastFilter[String] with Logging {
|
|
|
|
|
def filter(m: String) = {
|
|
|
|
|
var name = m
|
|
|
|
|
var message = ""
|
2009-07-29 20:17:40 +02:00
|
|
|
|
2009-08-01 17:40:16 +02:00
|
|
|
if (m.indexOf("__") > 0) {
|
|
|
|
|
name = m.substring(0, m.indexOf("__"))
|
|
|
|
|
message = m.substring(m.indexOf("__") + 2)
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-02 12:58:20 +02:00
|
|
|
("<script type='text/javascript'>\n (window.app || window.parent.app).update({ name: \"" +
|
|
|
|
|
name + "\", message: \"" +
|
|
|
|
|
message +
|
|
|
|
|
"\" }); \n</script>\n")
|
2009-08-01 17:40:16 +02:00
|
|
|
}
|
2009-08-02 16:14:12 +02:00
|
|
|
}
|