Atmosphere almost integrated, ClassLoader issue.

This commit is contained in:
Viktor Klang 2009-07-26 19:12:22 +02:00
parent 13bc65142e
commit f7bfb066a5
25 changed files with 173 additions and 82 deletions

View file

@ -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

View file

@ -1,10 +0,0 @@
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
<servlet>
<servlet-name>Akka Servlet</servlet-name>
<servlet-class>se.scalablesolutions.akka.kernel.jersey.AkkaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Akka Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,5 @@
<atmosphere-handlers>
<atmosphere-handler context-root="" class-name="org.atmosphere.handler.ReflectorServletProcessor">
<property name="servletClass" value="se.scalablesolutions.akka.kernel.jersey.AkkaServlet"/>
</atmosphere-handler>
</atmosphere-handlers>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- <Loader className="org.atmosphere.util.AtmosphereClassloader"/> -->
<Loader delegate="true"/>
</Context>

View file

@ -105,10 +105,15 @@
</dependency>
<!-- For Jersey -->
<dependency>
<!--<dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-servlet-webserver</artifactId>
<version>1.9.9</version>
</dependency>-->
<dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-comet-webserver</artifactId>
<version>1.9.15b</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
@ -130,11 +135,32 @@
<artifactId>jersey-scala</artifactId>
<version>1.1.2-ea-SNAPSHOT</version>
</dependency>
<dependency>
<!--<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-lift</artifactId>
<version>1.1.2-ea-SNAPSHOT</version>
</dependency>-->
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-core</artifactId>
<version>0.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-portable-runtime</artifactId>
<version>0.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-compat</artifactId>
<version>0.3-SNAPSHOT</version>
</dependency>
<!--<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-grizzly-adapter</artifactId>
<version>0.3-SNAPSHOT<</version>
</dependency>-->
<!-- For third-party logging -->
<dependency>
@ -195,7 +221,7 @@
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
<!--<goal>testCompile</goal>-->
</goals>
</execution>
</executions>

View file

@ -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)

View file

@ -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))
}
}
}
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)
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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
* </pre>
* 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>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(view(<success>Tick: {counter + 1}</success>))
} else {
storage.put(KEY, new Integer(0))
hasStartedTicking = true
reply(view(<lift:success>Tick: 0</lift:success>))
@GET
@Produces(Array("text/html"))
def count() = {
(this !! Tick).getOrElse(<error>Error in counter</error>)
}
}
override protected def postRestart(reason: AnyRef, config: Option[AnyRef]) = {
println("Restarting due to: " + reason.asInstanceOf[Exception].getMessage)
}
def view(data : NodeSeq) : NodeSeq = <lift:surround with="default" at="content"> { data } <lift:Howdy.greet/></lift:surround>
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>)
}
}
override protected def postRestart(reason: AnyRef, config: Option[AnyRef]) = {
println("Restarting due to: " + reason.asInstanceOf[Exception].getMessage)
}
}
class Howdy
{
def greet = <h2>Hello mommy</h2>
}
@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() = "<!-- 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. -->\n"
override def receive: PartialFunction[Any, Unit] = {
case Chat(who,what,msg) => {
log.info("Chat(" + who + ", " + what + ", " + msg + ")")
what match {
case "login" => reply(<h3>System Message: {who} has joined.</h3>)
case "post" => reply(<p>{who} says: {msg}</p>)
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(<p>Error</p>)
}