adapted EchoServer for netty. Upgraded to unflitered 0.6.1
--- a/project/build.scala Tue Apr 03 21:48:11 2012 +0200
+++ b/project/build.scala Wed Apr 04 18:23:20 2012 +0200
@@ -10,7 +10,7 @@
val dispatch_version = "0.8.6"
val dispatch_http = "net.databinder" %% "dispatch-http" % dispatch_version
val dispatch_nio = "net.databinder" %% "dispatch-nio" % dispatch_version
- val unfiltered_version = "0.5.3"
+ val unfiltered_version = "0.6.1"
val unfiltered_filter = "net.databinder" %% "unfiltered-filter" % unfiltered_version
val unfiltered_jetty = "net.databinder" %% "unfiltered-jetty" % unfiltered_version
val unfiltered_netty = "net.databinder" %% "unfiltered-netty" % unfiltered_version
--- a/src/main/scala/EchoPlan.scala Tue Apr 03 21:48:11 2012 +0200
+++ b/src/main/scala/EchoPlan.scala Wed Apr 04 18:23:20 2012 +0200
@@ -23,31 +23,59 @@
package org.w3.readwriteweb
-import unfiltered.request.Path
-import unfiltered.response.{ResponseString, PlainTextContent, ContentType, Ok}
import io.BufferedSource
+import unfiltered.Cycle
+import unfiltered.netty.ReceivedMessage
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+import unfiltered.request.{HttpRequest, Path}
+import org.jboss.netty.handler.codec.http.HttpResponse
+import unfiltered.response.{Ok, PlainTextContent, ResponseString}
/**
+ * Useful Echo Server, for debugging
+ *
* @author hjs
* @created: 19/10/2011
*/
+trait EchoPlan[Req,Res] {
+ import collection.JavaConversions._
-class EchoPlan {
- import collection.JavaConversions._
+ /**
+ * unfiltered is missing a method to get the header names from the request, so this method is required
+ * @param req
+ * @return
+ */
+ def headers(req: HttpRequest[Req]): Iterator[String]
- lazy val plan = unfiltered.filter.Planify({
+ def intent : Cycle.Intent[Req, Res] = {
case req@Path(path) if path startsWith "/test/http/echo" => {
Ok ~> PlainTextContent ~> {
- val headers = req.underlying.getHeaderNames()
- val result = for (name <- headers ;
- val nameStr = name.asInstanceOf[String]
+ val header = for (headerName <- headers(req);
+ headerValue <- req.headers(headerName)
) yield {
- nameStr + ": " + req.underlying.getHeader(nameStr)+"\r\n"
+ headerName + ": " + headerValue +"\r\n"
}
- ResponseString(result.mkString+ "\r\n" + new BufferedSource(req.inputStream).mkString)
+ ResponseString(header.mkString+ "\r\n" + new BufferedSource(req.inputStream).mkString)
}
}
+ }
+}
- })
-}
\ No newline at end of file
+/**
+ * this is a trait so that it can be mixed in with different threading policies
+ */
+trait NettyEchoPlan extends EchoPlan[ReceivedMessage,HttpResponse] {
+ import scala.collection.JavaConverters.collectionAsScalaIterableConverter
+
+ def headers(req: HttpRequest[ReceivedMessage]) = req.underlying.request.getHeaderNames.asScala.toIterator
+}
+
+
+object JettyEchoPlan extends EchoPlan[HttpServletRequest,HttpServletResponse] {
+ import scala.collection.JavaConverters.enumerationAsScalaIteratorConverter
+ import java.util.Enumeration
+ def headers(req: HttpRequest[HttpServletRequest]) = Option(req.underlying.getHeaderNames).
+ map(enum=> enumerationAsScalaIteratorConverter[String](enum.asInstanceOf[Enumeration[String]]).asScala).
+ get
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/ReadWriteWeb.scala Wed Apr 04 18:23:20 2012 +0200
@@ -0,0 +1,174 @@
+package org.w3.readwriteweb
+
+import auth.{AuthZ, NullAuthZ}
+import org.w3.readwriteweb.util._
+
+import scala.io.Source
+import java.net.URL
+
+import org.slf4j.{Logger, LoggerFactory}
+
+import com.hp.hpl.jena.query.{Query, QueryExecution, QueryExecutionFactory}
+import com.hp.hpl.jena.update.UpdateAction
+import Query.{QueryTypeSelect => SELECT,
+ QueryTypeAsk => ASK,
+ QueryTypeConstruct => CONSTRUCT,
+ QueryTypeDescribe => DESCRIBE}
+
+import scalaz.{Resource => _}
+import unfiltered.request._
+import unfiltered.Cycle
+import unfiltered.response._
+
+import com.hp.hpl.jena.rdf.model.Model
+
+//object ReadWriteWeb {
+//
+// val defaultHandler: PartialFunction[Throwable, HttpResponse[_]] = {
+// case t => InternalServerError ~> ResponseString(t.getStackTraceString)
+// }
+//
+//}
+
+/**
+ * The ReadWriteWeb intent.
+ * It is independent of jetty or netty
+ */
+trait ReadWriteWeb[Req, Res] {
+ val rm: ResourceManager
+ implicit def manif: Manifest[Req]
+ implicit val authz: AuthZ[Req, Res] = new NullAuthZ[Req, Res]
+ // a few type short cuts to make it easier to reason with the code here
+ // one may want to generalize this code so that it does not depend so strongly on servlets.
+// type Request = HttpRequest[Req]
+// type Response = ResponseFunction[Res]
+
+ val logger: Logger = LoggerFactory.getLogger(this.getClass)
+
+ /**
+ * The partial function that if triggered sends to the readwrite web code.
+ * It wraps the ReadWriteWeb function with the AuthZ passed in the argument
+ * ( Note that we don't want to protect this intent, since that would be to apply the security to all other applications,
+ * many of which may want different authorization implementations )
+ */
+ def intent : Cycle.Intent[Req, Res] = {
+ case req @ Path(path) if path startsWith rm.basePath => authz.protect(rwwIntent)(manif)(req)
+ }
+
+ /**
+ * The core ReadWrite web function
+ * ( This is not a partial function and so is not a Plan.Intent )
+ *
+ * This code makes use of ScalaZ Validation. For example of how to use it, see
+ * http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleValidation.scala.html
+ *
+ * the Resource abstraction returns Validation[Throwable, ?something]
+ * we use the for monadic constructs (although it's *not* a monad).
+ * Everything construct are mapped to Validation[ResponseFunction, ResponseFuntion],
+ * the left value always denoting the failure. Hence, the rest of the for-construct
+ * is not evaluated, but let the reader of the code understand clearly what's happening.
+ *
+ * This mapping is made possible with the failMap method. I couldn't find an equivalent
+ * in the ScalaZ API so I made my own through an implicit.
+ *
+ * At last, Validation[ResponseFunction, ResponseFuntion] is exposed as a ResponseFunction
+ * through another implicit conversion. It saves us the call to the Validation.fold() method
+ */
+ def rwwIntent = (req: HttpRequest[Req]) => {
+
+ val Authoritative(uri: URL, representation: Representation) = req
+ val r: Resource = rm.resource(uri)
+ val res: ResponseFunction[Res] = req match {
+ case GET(_) if representation == HTMLRepr => {
+ val source = Source.fromFile("src/main/resources/skin.html")("UTF-8")
+ val body = source.getLines.mkString("\n")
+ Ok ~> ViaSPARQL ~> ContentType("text/html") ~> ResponseString(body)
+ }
+ case GET(_) | HEAD(_) =>
+ for {
+ model <- r.get() failMap { x => NotFound }
+ lang = representation match {
+ case RDFRepr(l) => l
+ case _ => Lang.default
+ }
+ } yield {
+ val res = req match {
+ case GET(_) => Ok ~> ViaSPARQL ~> ContentType(lang.contentType) ~> ResponseModel(model, uri, lang)
+ case HEAD(_) => Ok ~> ViaSPARQL ~> ContentType(lang.contentType)
+ }
+ res ~> ContentLocation( uri.toString ) // without this netty (perhaps jetty too?) sends very weird headers, breaking tests
+ }
+ case PUT(_) & RequestLang(lang) if representation == DirectoryRepr => {
+ for {
+ bodyModel <- modelFromInputStream(Body.stream(req), uri, lang) failMap { t => BadRequest ~> ResponseString(t.getStackTraceString) }
+ _ <- r.createDirectory(bodyModel) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString) }
+ } yield Created
+ }
+ case PUT(_) & RequestLang(lang) =>
+ for {
+ bodyModel <- modelFromInputStream(Body.stream(req), uri, lang) failMap { t => BadRequest ~> ResponseString(t.getStackTraceString) }
+ _ <- r.save(bodyModel) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString) }
+ } yield Created
+ case PUT(_) =>
+ BadRequest ~> ResponseString("Content-Type MUST be one of: " + Lang.supportedAsString)
+ case POST(_) & RequestContentType(ct) if Post.supportContentTypes contains ct => {
+ Post.parse(Body.stream(req), uri, ct) match {
+ case PostUnknown => {
+ logger.info("Couldn't parse the request")
+ BadRequest ~> ResponseString("You MUST provide valid content for given Content-Type: " + ct)
+ }
+ case PostUpdate(update) => {
+ logger.info("SPARQL UPDATE:\n" + update.toString())
+ for {
+ model <- r.get() failMap { t => NotFound }
+ // TODO: we should handle an error here
+ _ = UpdateAction.execute(update, model)
+ _ <- r.save(model) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString)}
+ } yield Ok
+ }
+ case PostRDF(diffModel) => {
+ logger.info("RDF content:\n" + diffModel.toString())
+ for {
+ model <- r.get() failMap { t => NotFound }
+ // TODO: we should handle an error here
+ _ = model.add(diffModel)
+ _ <- r.save(model) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString)}
+ } yield Ok
+ }
+ case PostQuery(query) => {
+ logger.info("SPARQL Query:\n" + query.toString())
+ lazy val lang = RequestLang(req) getOrElse Lang.default
+ for {
+ model <- r.get() failMap { t => NotFound }
+ } yield {
+ val qe: QueryExecution = QueryExecutionFactory.create(query, model)
+ query.getQueryType match {
+ case SELECT =>
+ Ok ~> ContentType("application/sparql-results+xml") ~> ResponseResultSet(qe.execSelect())
+ case ASK =>
+ Ok ~> ContentType("application/sparql-results+xml") ~> ResponseResultSet(qe.execAsk())
+ case CONSTRUCT => {
+ val result: Model = qe.execConstruct()
+ Ok ~> ContentType(lang.contentType) ~> ResponseModel(model, uri, lang)
+ }
+ case DESCRIBE => {
+ val result: Model = qe.execDescribe()
+ Ok ~> ContentType(lang.contentType) ~> ResponseModel(model, uri, lang)
+ }
+ }
+ }
+ }
+ }
+ }
+ case POST(_) =>
+ BadRequest ~> ResponseString("Content-Type MUST be one of: " + Post.supportedAsString)
+ case _ => MethodNotAllowed ~> Allow("GET", "PUT", "POST")
+ }
+ res
+ }
+
+
+
+
+
+}
--- a/src/main/scala/ReadWriteWebMain.scala Tue Apr 03 21:48:11 2012 +0200
+++ b/src/main/scala/ReadWriteWebMain.scala Wed Apr 04 18:23:20 2012 +0200
@@ -125,9 +125,10 @@
service.filter(new FilterLogger(logger)).
context("/public"){ ctx:ContextBuilder =>
ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
- }.filter(Planify(rww.intent)).
+ }.filter(Planify(JettyEchoPlan.intent)).
+ filter(Planify(rww.intent)).
filter(Planify(x509v.intent)).
- filter(new EchoPlan().plan).run()
+ run()
}
--- a/src/main/scala/netty/ReadWriteWebNetty.scala Tue Apr 03 21:48:11 2012 +0200
+++ b/src/main/scala/netty/ReadWriteWebNetty.scala Wed Apr 04 18:23:20 2012 +0200
@@ -66,6 +66,8 @@
override val authz = new RDFAuthZ[ReceivedMessage,HttpResponse](filesystem)
}
+ val echo = new cycle.Plan with cycle.ThreadPool with ServerErrorResponse with NettyEchoPlan
+
//this is incomplete: we should be able to start both ports.... not sure how to do this yet.
val service = httpsPort.value match {
case Some(port) => new KeyAuth_Https(port)
@@ -75,6 +77,7 @@
// configures and launches a Netty server
service.plan(publicStatic).
+ plan( echo ).
plan( x509v ).
plan( webidp ).
plan( rww ).run()
--- a/src/main/scala/plan.scala Tue Apr 03 21:48:11 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-package org.w3.readwriteweb
-
-import auth.{AuthZ, NullAuthZ}
-import org.w3.readwriteweb.util._
-
-import scala.io.Source
-import java.net.URL
-
-import org.slf4j.{Logger, LoggerFactory}
-
-import com.hp.hpl.jena.query.{Query, QueryExecution, QueryExecutionFactory}
-import com.hp.hpl.jena.update.UpdateAction
-import Query.{QueryTypeSelect => SELECT,
- QueryTypeAsk => ASK,
- QueryTypeConstruct => CONSTRUCT,
- QueryTypeDescribe => DESCRIBE}
-
-import scalaz.{Resource => _}
-import unfiltered.request._
-import unfiltered.Cycle
-import unfiltered.response._
-
-import com.hp.hpl.jena.rdf.model.Model
-
-//object ReadWriteWeb {
-//
-// val defaultHandler: PartialFunction[Throwable, HttpResponse[_]] = {
-// case t => InternalServerError ~> ResponseString(t.getStackTraceString)
-// }
-//
-//}
-
-/**
- * The ReadWriteWeb intent.
- * It is independent of jetty or netty
- */
-trait ReadWriteWeb[Req, Res] {
- val rm: ResourceManager
- implicit def manif: Manifest[Req]
- implicit val authz: AuthZ[Req, Res] = new NullAuthZ[Req, Res]
- // a few type short cuts to make it easier to reason with the code here
- // one may want to generalize this code so that it does not depend so strongly on servlets.
-// type Request = HttpRequest[Req]
-// type Response = ResponseFunction[Res]
-
- val logger: Logger = LoggerFactory.getLogger(this.getClass)
-
- /**
- * The partial function that if triggered sends to the readwrite web code.
- * It wraps the ReadWriteWeb function with the AuthZ passed in the argument
- * ( Note that we don't want to protect this intent, since that would be to apply the security to all other applications,
- * many of which may want different authorization implementations )
- */
- def intent : Cycle.Intent[Req, Res] = {
- case req @ Path(path) if path startsWith rm.basePath => authz.protect(rwwIntent)(manif)(req)
- }
-
- /**
- * The core ReadWrite web function
- * ( This is not a partial function and so is not a Plan.Intent )
- *
- * This code makes use of ScalaZ Validation. For example of how to use it, see
- * http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleValidation.scala.html
- *
- * the Resource abstraction returns Validation[Throwable, ?something]
- * we use the for monadic constructs (although it's *not* a monad).
- * Everything construct are mapped to Validation[ResponseFunction, ResponseFuntion],
- * the left value always denoting the failure. Hence, the rest of the for-construct
- * is not evaluated, but let the reader of the code understand clearly what's happening.
- *
- * This mapping is made possible with the failMap method. I couldn't find an equivalent
- * in the ScalaZ API so I made my own through an implicit.
- *
- * At last, Validation[ResponseFunction, ResponseFuntion] is exposed as a ResponseFunction
- * through another implicit conversion. It saves us the call to the Validation.fold() method
- */
- def rwwIntent = (req: HttpRequest[Req]) => {
-
- val Authoritative(uri: URL, representation: Representation) = req
- val r: Resource = rm.resource(uri)
- val res: ResponseFunction[Res] = req match {
- case GET(_) if representation == HTMLRepr => {
- val source = Source.fromFile("src/main/resources/skin.html")("UTF-8")
- val body = source.getLines.mkString("\n")
- Ok ~> ViaSPARQL ~> ContentType("text/html") ~> ResponseString(body)
- }
- case GET(_) | HEAD(_) =>
- for {
- model <- r.get() failMap { x => NotFound }
- lang = representation match {
- case RDFRepr(l) => l
- case _ => Lang.default
- }
- } yield {
- val res = req match {
- case GET(_) => Ok ~> ViaSPARQL ~> ContentType(lang.contentType) ~> ResponseModel(model, uri, lang)
- case HEAD(_) => Ok ~> ViaSPARQL ~> ContentType(lang.contentType)
- }
- res ~> ContentLocation( uri.toString ) // without this netty (perhaps jetty too?) sends very weird headers, breaking tests
- }
- case PUT(_) & RequestLang(lang) if representation == DirectoryRepr => {
- for {
- bodyModel <- modelFromInputStream(Body.stream(req), uri, lang) failMap { t => BadRequest ~> ResponseString(t.getStackTraceString) }
- _ <- r.createDirectory(bodyModel) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString) }
- } yield Created
- }
- case PUT(_) & RequestLang(lang) =>
- for {
- bodyModel <- modelFromInputStream(Body.stream(req), uri, lang) failMap { t => BadRequest ~> ResponseString(t.getStackTraceString) }
- _ <- r.save(bodyModel) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString) }
- } yield Created
- case PUT(_) =>
- BadRequest ~> ResponseString("Content-Type MUST be one of: " + Lang.supportedAsString)
- case POST(_) & RequestContentType(ct) if Post.supportContentTypes contains ct => {
- Post.parse(Body.stream(req), uri, ct) match {
- case PostUnknown => {
- logger.info("Couldn't parse the request")
- BadRequest ~> ResponseString("You MUST provide valid content for given Content-Type: " + ct)
- }
- case PostUpdate(update) => {
- logger.info("SPARQL UPDATE:\n" + update.toString())
- for {
- model <- r.get() failMap { t => NotFound }
- // TODO: we should handle an error here
- _ = UpdateAction.execute(update, model)
- _ <- r.save(model) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString)}
- } yield Ok
- }
- case PostRDF(diffModel) => {
- logger.info("RDF content:\n" + diffModel.toString())
- for {
- model <- r.get() failMap { t => NotFound }
- // TODO: we should handle an error here
- _ = model.add(diffModel)
- _ <- r.save(model) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString)}
- } yield Ok
- }
- case PostQuery(query) => {
- logger.info("SPARQL Query:\n" + query.toString())
- lazy val lang = RequestLang(req) getOrElse Lang.default
- for {
- model <- r.get() failMap { t => NotFound }
- } yield {
- val qe: QueryExecution = QueryExecutionFactory.create(query, model)
- query.getQueryType match {
- case SELECT =>
- Ok ~> ContentType("application/sparql-results+xml") ~> ResponseResultSet(qe.execSelect())
- case ASK =>
- Ok ~> ContentType("application/sparql-results+xml") ~> ResponseResultSet(qe.execAsk())
- case CONSTRUCT => {
- val result: Model = qe.execConstruct()
- Ok ~> ContentType(lang.contentType) ~> ResponseModel(model, uri, lang)
- }
- case DESCRIBE => {
- val result: Model = qe.execDescribe()
- Ok ~> ContentType(lang.contentType) ~> ResponseModel(model, uri, lang)
- }
- }
- }
- }
- }
- }
- case POST(_) =>
- BadRequest ~> ResponseString("Content-Type MUST be one of: " + Post.supportedAsString)
- case _ => MethodNotAllowed ~> Allow("GET", "PUT", "POST")
- }
- res
- }
-
-
-
-
-
-}