diff --git a/bin/start-akka-server.sh b/bin/start-akka-server.sh
index 5eb5b8278d..67ebb42e24 100755
--- a/bin/start-akka-server.sh
+++ b/bin/start-akka-server.sh
@@ -15,6 +15,7 @@ mkdir $BASE_DIR/storage/commitlog
mkdir $BASE_DIR/storage/data
mkdir $BASE_DIR/storage/system
+CLASSPATH=$BASE_DIR/config:$BASE_DIR/deploy/root:$BASE_DIR/deploy/root/META-INF:$BASE_DIR/deploy/root/WEB-INF/classes
CLASSPATH=$CLASSPATH:$BASE_DIR/config
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/akka-kernel-0.5.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/akka-util-java-0.5.jar
@@ -35,11 +36,12 @@ CLASSPATH=$CLASSPATH:$BASE_DIR/lib/commons-logging-1.0.4.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/commons-math-1.1.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/configgy-1.3.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/google-collect-snapshot-20090211.jar
-CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-framework-1.8.6.3.jar
-CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-http-1.8.6.3.jar
-CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-http-servlet-1.8.6.3.jar
-CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-http-utils-1.8.6.3.jar
-CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-servlet-webserver-1.8.6.3.jar
+#CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-framework-1.8.6.3.jar
+#CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-http-1.8.6.3.jar
+#CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-http-servlet-1.8.6.3.jar
+#CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-http-utils-1.8.6.3.jar
+#CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-servlet-webserver-1.8.6.3.jar
+CLASSPATH=$CLASSPATH:$BASE_DIR/lib/grizzly-comet-webserver-1.9.15b.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/guice-core-2.0-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/guice-jsr250-2.0-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/high-scale-lib.jar
@@ -50,7 +52,10 @@ CLASSPATH=$CLASSPATH:$BASE_DIR/lib/jersey-core-1.0.3.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/jersey-json-1.0.3.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/jersey-server-1.0.3.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/jersey-scala-1.1.2-ea-SNAPSHOT.jar
-CLASSPATH=$CLASSPATH:$BASE_DIR/lib/jersey-lift-1.1.2-ea-SNAPSHOT.jar
+CLASSPATH=$CLASSPATH:$BASE_DIR/lib/atmosphere-core-0.3-SNAPSHOT.jar
+CLASSPATH=$CLASSPATH:$BASE_DIR/lib/atmosphere-portable-runtime-0.3-SNAPSHOT.jar
+CLASSPATH=$CLASSPATH:$BASE_DIR/lib/atmosphere-compat-0.3-SNAPSHOT.jar
+#CLASSPATH=$CLASSPATH:$BASE_DIR/lib/atmosphere-grizzly-adapter-0.3-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/JSAP-2.1.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/jsr250-api-1.0.jar
CLASSPATH=$CLASSPATH:$BASE_DIR/lib/jsr311-api-1.0.jar
diff --git a/config/web.xml b/config/web.xml
deleted file mode 100644
index 831543f358..0000000000
--- a/config/web.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Akka Servlet
- se.scalablesolutions.akka.kernel.jersey.AkkaServlet
-
-
- Akka Servlet
- /*
-
-
diff --git a/deploy/akka-samples-java-0.5.jar b/deploy/akka-samples-java-0.5.jar
index e0fecd9427..d3778ee6da 100644
Binary files a/deploy/akka-samples-java-0.5.jar and b/deploy/akka-samples-java-0.5.jar differ
diff --git a/deploy/akka-samples-scala-0.5.jar b/deploy/akka-samples-scala-0.5.jar
index a5bc4b3c6f..b129a3dcbe 100644
Binary files a/deploy/akka-samples-scala-0.5.jar and b/deploy/akka-samples-scala-0.5.jar differ
diff --git a/deploy/root/META-INF/atmosphere.xml b/deploy/root/META-INF/atmosphere.xml
new file mode 100644
index 0000000000..a3ca57b580
--- /dev/null
+++ b/deploy/root/META-INF/atmosphere.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/deploy/root/META-INF/context.xml b/deploy/root/META-INF/context.xml
new file mode 100644
index 0000000000..71838e0728
--- /dev/null
+++ b/deploy/root/META-INF/context.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/kernel/pom.xml b/kernel/pom.xml
index 4692cf98a1..07ff4fac07 100644
--- a/kernel/pom.xml
+++ b/kernel/pom.xml
@@ -105,10 +105,15 @@
-
+
+
+ com.sun.grizzly
+ grizzly-comet-webserver
+ 1.9.15b
com.sun.jersey
@@ -130,11 +135,32 @@
jersey-scala
1.1.2-ea-SNAPSHOT
-
+
+
+
+ org.atmosphere
+ atmosphere-core
+ 0.3-SNAPSHOT
+
+ org.atmosphere
+ atmosphere-portable-runtime
+ 0.3-SNAPSHOT
+
+
+ org.atmosphere
+ atmosphere-compat
+ 0.3-SNAPSHOT
+
+
@@ -195,7 +221,7 @@
compile
- testCompile
+
diff --git a/kernel/src/main/scala/Kernel.scala b/kernel/src/main/scala/Kernel.scala
index a20292181f..7db6ed9e00 100644
--- a/kernel/src/main/scala/Kernel.scala
+++ b/kernel/src/main/scala/Kernel.scala
@@ -14,7 +14,7 @@ import java.net.URLClassLoader
import net.lag.configgy.{Config, Configgy, RuntimeEnvironment}
-import kernel.jersey.AkkaServlet
+import kernel.jersey.{AkkaServlet,AkkaCometServlet}
import kernel.nio.RemoteServer
import kernel.state.CassandraStorage
import kernel.util.Logging
@@ -36,7 +36,7 @@ object Kernel extends Logging {
val REST_HOSTNAME = kernel.Kernel.config.getString("akka.rest.hostname", "localhost")
val REST_URL = "http://" + REST_HOSTNAME
val REST_PORT = kernel.Kernel.config.getInt("akka.rest.port", 9998)
-
+ val REST_ROOT = System.getenv("AKKA_HOME") + "/deploy/root/"
// FIXME add API to shut server down gracefully
private var remoteServer: RemoteServer = _
@@ -91,8 +91,8 @@ object Kernel extends Logging {
private[akka] def runApplicationBootClasses = {
val HOME = try { System.getenv("AKKA_HOME") } catch { case e: NullPointerException => throw new IllegalStateException("AKKA_HOME system variable needs to be set. Should point to the root of the Akka distribution.") }
- val CLASSES = HOME + "/kernel/target/classes" // FIXME remove for dist
- val LIB = HOME + "/lib"
+ //val CLASSES = HOME + "/kernel/target/classes" // FIXME remove for dist
+ //val LIB = HOME + "/lib"
val CONFIG = HOME + "/config"
val DEPLOY = HOME + "/deploy"
val DEPLOY_DIR = new File(DEPLOY)
@@ -123,22 +123,25 @@ object Kernel extends Logging {
private[akka] def startJersey = {
val uri = UriBuilder.fromUri(REST_URL).port(REST_PORT).build()
- val adapter = new ServletAdapter
- val servlet = new AkkaServlet
-
- adapter.addInitParameter("bootloader", config.getString("akka.lift.bootloader").getOrElse(null))
- adapter.addInitParameter("com.sun.jersey.config.feature.Redirect", "true")
- adapter.addInitParameter("com.sun.jersey.config.feature.ImplicitViewables", "true")
- adapter.setServletInstance(servlet)
- adapter.setContextPath(uri.getPath)
-
- //TODO add initialization of Lift (LiftFilter needs initialization)
-
- log.debug("REST service context path: [" + uri.getPath + "]")
val scheme = uri.getScheme
if (!scheme.equalsIgnoreCase("http")) throw new IllegalArgumentException("The URI scheme, of the URI " + REST_URL + ", must be equal (ignoring case) to 'http'")
+
+ val adapter = new ServletAdapter()
+ //val servlet = org.atmosphere.cpr.AtmosphereServlet
+ //val servlet = new AkkaServlet()
+ //adapter.addInitParameter("bootloader", config.getString("akka.lift.bootloader").getOrElse(null))
+ adapter.addInitParameter("com.sun.jersey.spi.container.ResourceFilters","org.atmosphere.core.AtmosphereFilter")
+ adapter.addInitParameter("com.sun.jersey.config.feature.Redirect", "true")
+ adapter.addInitParameter("com.sun.jersey.config.feature.ImplicitViewables", "true")
+
+ adapter.setServletInstance(new AkkaCometServlet)
+ adapter.setContextPath(uri.getPath)
+ adapter.setRootFolder(REST_ROOT)
+ log.info("REST service root path: [" + adapter.getRootFolder + "] and context path [" + adapter.getContextPath + "] ")
+
+
jerseySelectorThread = new SelectorThread
jerseySelectorThread.setAlgorithmClassName(classOf[StaticStreamAlgorithm].getName)
jerseySelectorThread.setPort(REST_PORT)
diff --git a/kernel/src/main/scala/jersey/AkkaServlet.scala b/kernel/src/main/scala/jersey/AkkaServlet.scala
index 1d51949e25..52e3fb540d 100644
--- a/kernel/src/main/scala/jersey/AkkaServlet.scala
+++ b/kernel/src/main/scala/jersey/AkkaServlet.scala
@@ -6,12 +6,17 @@ package se.scalablesolutions.akka.kernel.jersey
import kernel.Kernel
import config.ConfiguratorRepository
+import util.Logging
import com.sun.jersey.api.core.{DefaultResourceConfig, ResourceConfig}
import com.sun.jersey.spi.container.servlet.ServletContainer
import com.sun.jersey.spi.container.WebApplication
+
+import javax.servlet.{ServletConfig}
+import java.net.{URL,URLClassLoader}
import java.util.HashSet
+
class AkkaServlet extends ServletContainer{
override def initiate(rc: ResourceConfig, wa: WebApplication) = {
@@ -29,4 +34,17 @@ class AkkaServlet extends ServletContainer{
new DefaultResourceConfig(set),
new ActorComponentProviderFactory(configurators))
}
-}
\ No newline at end of file
+}
+
+class AkkaCometServlet extends org.atmosphere.cpr.AtmosphereServlet with Logging
+ {
+ override def init(sconf : ServletConfig) = {
+
+ val url = sconf.getServletContext().getResource("/WEB-INF/classes/")
+ val urlC = new URLClassLoader(Array(url),Thread.currentThread().getContextClassLoader())
+
+ loadAtmosphereDotXml(sconf.getServletContext.getResourceAsStream("META-INF/atmosphere.xml"), urlC);
+
+ super.init(sconf)
+ }
+ }
diff --git a/lib/akka-kernel-0.5.jar b/lib/akka-kernel-0.5.jar
index f21c05cd8a..f285730f6f 100644
Binary files a/lib/akka-kernel-0.5.jar and b/lib/akka-kernel-0.5.jar differ
diff --git a/lib/akka-util-java-0.5.jar b/lib/akka-util-java-0.5.jar
index 02ce908044..aa8ba3bb03 100644
Binary files a/lib/akka-util-java-0.5.jar and b/lib/akka-util-java-0.5.jar differ
diff --git a/lib/atmosphere-compat-0.3-SNAPSHOT.jar b/lib/atmosphere-compat-0.3-SNAPSHOT.jar
new file mode 100644
index 0000000000..7125488274
Binary files /dev/null and b/lib/atmosphere-compat-0.3-SNAPSHOT.jar differ
diff --git a/lib/atmosphere-core-0.3-SNAPSHOT.jar b/lib/atmosphere-core-0.3-SNAPSHOT.jar
new file mode 100644
index 0000000000..6876b9e845
Binary files /dev/null and b/lib/atmosphere-core-0.3-SNAPSHOT.jar differ
diff --git a/lib/atmosphere-grizzly-adapter-0.3-SNAPSHOT.jar b/lib/atmosphere-grizzly-adapter-0.3-SNAPSHOT.jar
new file mode 100644
index 0000000000..be82de123e
Binary files /dev/null and b/lib/atmosphere-grizzly-adapter-0.3-SNAPSHOT.jar differ
diff --git a/lib/atmosphere-portable-runtime-0.3-SNAPSHOT.jar b/lib/atmosphere-portable-runtime-0.3-SNAPSHOT.jar
new file mode 100644
index 0000000000..c12212705a
Binary files /dev/null and b/lib/atmosphere-portable-runtime-0.3-SNAPSHOT.jar differ
diff --git a/lib/grizzly-comet-webserver-1.9.15b.jar b/lib/grizzly-comet-webserver-1.9.15b.jar
new file mode 100644
index 0000000000..0c835ae44b
Binary files /dev/null and b/lib/grizzly-comet-webserver-1.9.15b.jar differ
diff --git a/lib/grizzly-framework-1.8.6.3.jar b/lib/grizzly-framework-1.8.6.3.jar
deleted file mode 100644
index ded488b1b2..0000000000
Binary files a/lib/grizzly-framework-1.8.6.3.jar and /dev/null differ
diff --git a/lib/grizzly-http-1.8.6.3.jar b/lib/grizzly-http-1.8.6.3.jar
deleted file mode 100644
index 7e6fe75107..0000000000
Binary files a/lib/grizzly-http-1.8.6.3.jar and /dev/null differ
diff --git a/lib/grizzly-http-servlet-1.8.6.3.jar b/lib/grizzly-http-servlet-1.8.6.3.jar
deleted file mode 100644
index 9011703cc9..0000000000
Binary files a/lib/grizzly-http-servlet-1.8.6.3.jar and /dev/null differ
diff --git a/lib/grizzly-http-utils-1.8.6.3.jar b/lib/grizzly-http-utils-1.8.6.3.jar
deleted file mode 100644
index 00780f5d40..0000000000
Binary files a/lib/grizzly-http-utils-1.8.6.3.jar and /dev/null differ
diff --git a/lib/grizzly-servlet-webserver-1.8.6.3.jar b/lib/grizzly-servlet-webserver-1.8.6.3.jar
deleted file mode 100644
index be0a596ef1..0000000000
Binary files a/lib/grizzly-servlet-webserver-1.8.6.3.jar and /dev/null differ
diff --git a/lib/jersey-lift-1.1.2-ea-SNAPSHOT.jar b/lib/jersey-lift-1.1.2-ea-SNAPSHOT.jar
deleted file mode 100644
index 72403fe793..0000000000
Binary files a/lib/jersey-lift-1.1.2-ea-SNAPSHOT.jar and /dev/null differ
diff --git a/lib/lift-util-1.1-M3.jar b/lib/lift-util-1.1-M3.jar
deleted file mode 100644
index a37eca8b18..0000000000
Binary files a/lib/lift-util-1.1-M3.jar and /dev/null differ
diff --git a/lib/lift-webkit-1.1-M3.jar b/lib/lift-webkit-1.1-M3.jar
deleted file mode 100644
index 28da5b281b..0000000000
Binary files a/lib/lift-webkit-1.1-M3.jar and /dev/null differ
diff --git a/samples-scala/src/main/scala/SimpleService.scala b/samples-scala/src/main/scala/SimpleService.scala
index 56734a6025..ee3be1f83e 100644
--- a/samples-scala/src/main/scala/SimpleService.scala
+++ b/samples-scala/src/main/scala/SimpleService.scala
@@ -1,26 +1,31 @@
package sample.scala
-import javax.ws.rs.{Path, GET, Produces,QueryParam,DefaultValue}
+import javax.ws.rs.{GET, POST, Path, Produces, WebApplicationException, Consumes}
import se.scalablesolutions.akka.kernel.state.{TransactionalState, TransactionalMap, CassandraStorageConfig}
import se.scalablesolutions.akka.kernel.actor.{Supervisor, SupervisorFactory, Actor, StartSupervisor}
import se.scalablesolutions.akka.kernel.config.ScalaConfig._
+import javax.ws.rs.core.MultivaluedMap
+
import _root_.scala.xml.{NodeSeq}
import se.scalablesolutions.akka.kernel.util.{Logging}
+import org.atmosphere.core.annotation.{Broadcast, BroadcastFilter, Suspend}
+import org.atmosphere.util.{XSSHtmlFilter}
+
class Boot {
- object factory extends SupervisorFactory {
- override def getSupervisorConfig: SupervisorConfig = {
- SupervisorConfig(
- RestartStrategy(OneForOne, 3, 100),
- Supervise(
- new SimpleService,
- LifeCycle(Permanent, 100))
- :: Nil)
+ object factory extends SupervisorFactory {
+ override def getSupervisorConfig: SupervisorConfig = {
+ SupervisorConfig(
+ RestartStrategy(OneForOne, 3, 100),
+ Supervise(
+ new Chat,
+ LifeCycle(Permanent, 100))
+ :: Nil)
+ }
}
- }
- val supervisor = factory.newSupervisor
- supervisor.startSupervisor
+ val supervisor = factory.newSupervisor
+ supervisor.startSupervisor
}
/**
@@ -29,45 +34,79 @@ class Boot {
* curl http://localhost:9998/scalacount
*
* Or browse to the URL from a web browser.
- */
+ */
@Path("/scalacount")
class SimpleService extends Actor {
- uuid = "SimpleService"
- makeTransactionRequired
+ uuid = "SimpleService"
+ makeTransactionRequired
- case object Tick
- private val KEY = "COUNTER";
- private var hasStartedTicking = false;
- private val storage = TransactionalState.newPersistentMap(CassandraStorageConfig())
+ case object Tick
+ private val KEY = "COUNTER";
+ private var hasStartedTicking = false;
+ private val storage = TransactionalState.newPersistentMap(CassandraStorageConfig())
- @GET
- @Produces(Array("text/html"))
- def count(@DefaultValue("unknown") @QueryParam("who") who : String) = {
- log.info(who)
-
- (this !! Tick).getOrElse(view(Error in counter))
- }
-
- 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(view(Tick: {counter + 1}))
- } else {
- storage.put(KEY, new Integer(0))
- hasStartedTicking = true
- reply(view(Tick: 0))
+ @GET
+ @Produces(Array("text/html"))
+ def count() = {
+ (this !! Tick).getOrElse(Error in counter)
}
- }
-
- override protected def postRestart(reason: AnyRef, config: Option[AnyRef]) = {
- println("Restarting due to: " + reason.asInstanceOf[Exception].getMessage)
- }
- def view(data : NodeSeq) : NodeSeq = { data }
+ 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(Tick: {counter + 1})
+ } else {
+ storage.put(KEY, new Integer(0))
+ hasStartedTicking = true
+ reply(Tick: 0)
+ }
+ }
+
+ override protected def postRestart(reason: AnyRef, config: Option[AnyRef]) = {
+ println("Restarting due to: " + reason.asInstanceOf[Exception].getMessage)
+ }
}
-class Howdy
-{
- def greet = Hello mommy
-}
\ No newline at end of file
+@Path("/chat")
+class Chat extends Actor with Logging{
+ uuid = "Chat"
+ makeTransactionRequired
+
+ case class Chat(val who : String, val what : String,val msg : String)
+ case object Suspend
+
+ private var hasStarted = false;
+ private val storage = TransactionalState.newPersistentMap(CassandraStorageConfig())
+
+ override protected def postRestart(reason: AnyRef, config: Option[AnyRef]) = {
+ println("Restarting due to: " + reason.asInstanceOf[Exception].getMessage)
+ }
+
+ @Suspend
+ @GET
+ @Produces(Array("text/html"))
+ def suspend() = "\n"
+
+ override def receive: PartialFunction[Any, Unit] = {
+ case Chat(who,what,msg) => {
+
+ log.info("Chat(" + who + ", " + what + ", " + msg + ")")
+
+ what match {
+ case "login" => reply(System Message: {who} has joined.
)
+ case "post" => reply({who} says: {msg}
)
+ case _ => throw new WebApplicationException(422)
+ }
+ }
+ }
+
+ @Broadcast
+ @Consumes(Array("application/x-www-form-urlencoded"))
+ @POST
+ @Produces(Array("text/html"))
+ //@BroadcastFilter(Array(classOf[XSSHtmlFilter]))//,classOf[JsonpFilter]))
+ def publishMessage(form: MultivaluedMap[String, String]) = (this !! Chat(form.getFirst("name"),form.getFirst("action"),form.getFirst("message"))).getOrElse(Error
)
+ }
\ No newline at end of file