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