--- a/project/build.scala Wed Aug 31 09:49:01 2011 -0400
+++ b/project/build.scala Sun Sep 04 15:07:07 2011 -0400
@@ -15,6 +15,7 @@
val jena = "com.hp.hpl.jena" % "jena" % "2.6.4"
val arq = "com.hp.hpl.jena" % "arq" % "2.8.8"
val grizzled = "org.clapper" %% "grizzled-scala" % "1.0.7" % "test"
+ val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.2"
}
// some usefull repositories
@@ -74,6 +75,7 @@
libraryDependencies += arq,
libraryDependencies += antiXML,
libraryDependencies += grizzled,
+ libraryDependencies += scalaz,
jarName in Assembly := "read-write-web.jar"
)
--- a/src/main/scala/Main.scala Wed Aug 31 09:49:01 2011 -0400
+++ b/src/main/scala/Main.scala Sun Sep 04 15:07:07 2011 -0400
@@ -19,6 +19,9 @@
import Query.{QueryTypeSelect => SELECT, QueryTypeAsk => ASK,
QueryTypeConstruct => CONSTRUCT, QueryTypeDescribe => DESCRIBE}
+import scalaz._
+import Scalaz._
+
import org.w3.readwriteweb.util._
class ReadWriteWeb(rm:ResourceManager) {
@@ -40,77 +43,80 @@
val body = source.getLines.mkString("\n")
Ok ~> ViaSPARQL ~> ContentType("text/html") ~> ResponseString(body)
}
- case GET(_) | HEAD(_) =>
- try {
- val model:Model = r.get()
- val encoding = RDFEncoding(req)
+ case GET(_) | HEAD(_) => {
+ val result = for {
+ model <- r.get() failMap { x => NotFound }
+ encoding = RDFEncoding(req)
+ } yield {
req match {
case GET(_) => Ok ~> ViaSPARQL ~> ContentType(encoding.toContentType) ~> ResponseModel(model, baseURI, encoding)
case HEAD(_) => Ok ~> ViaSPARQL ~> ContentType(encoding.toContentType)
}
- } catch {
- case fnfe:FileNotFoundException => NotFound
- case t:Throwable => {
- req match {
- case GET(_) => InternalServerError ~> ViaSPARQL
- case HEAD(_) => InternalServerError ~> ViaSPARQL ~> ResponseString(t.getStackTraceString)
- }
+ }
+ result.fold(f => f, s => s)
+ }
+ case PUT(_) => {
+ val response = for {
+ bodyModel <- modelFromInputStream(Body.stream(req), baseURI)
+ _ <- r.save(bodyModel)
+ } yield Created
+ response.fold(
+ t => InternalServerError ~> ResponseString(t.getStackTraceString),
+ s => s)
+ }
+ case POST(_) => {
+ Post.parse(Body.stream(req), baseURI) match {
+ case PostUnknown => {
+ logger.info("Couldn't parse the request")
+ BadRequest ~> ResponseString("You MUST provide valid content for either: SPARQL UPDATE, SPARQL Query, RDF/XML, TURTLE")
+ }
+ case PostUpdate(update) => {
+ logger.info("SPARQL UPDATE:\n" + update.toString())
+ val response =
+ for {
+ model <- r.get() failMap { t => NotFound }
+ _ <- UpdateAction.execute(update, model).success
+ _ <- r.save(model) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString)}
+ } yield Ok
+ response.fold(f => f, s => s)
+ }
+ case PostRDF(diffModel) => {
+ logger.info("RDF content:\n" + diffModel.toString())
+ val response =
+ for {
+ model <- r.get() failMap { t => NotFound }
+ _ <- model.add(diffModel).success
+ _ <- r.save(model) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString)}
+ } yield Ok
+ response.fold(f => f, s => s)
+ }
+ case PostQuery(query) => {
+ logger.info("SPARQL Query:\n" + query.toString())
+ lazy val encoding = RDFEncoding(req)
+ val response =
+ 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(encoding.toContentType) ~> ResponseModel(model, baseURI, encoding)
+ }
+ case DESCRIBE => {
+ val result:Model = qe.execDescribe()
+ Ok ~> ContentType(encoding.toContentType) ~> ResponseModel(model, baseURI, encoding)
+ }
+ }
+ }
+ response.fold(f => f, s => s)
}
}
- case PUT(_) =>
- try {
- val bodyModel = modelFromInputStream(Body.stream(req), baseURI)
- r.save(bodyModel)
- Created
- } catch {
- case t:Throwable => InternalServerError ~> ResponseString(t.getStackTraceString)
- }
- case POST(_) =>
- try {
- Post.parse(Body.stream(req), baseURI) match {
- case PostUnknown => {
- logger.info("Couldn't parse the request")
- BadRequest ~> ResponseString("You MUST provide valid content for either: SPARQL UPDATE, SPARQL Query, RDF/XML, TURTLE")
- }
- case PostUpdate(update) => {
- logger.info("SPARQL UPDATE:\n" + update.toString())
- val model = r.get()
- UpdateAction.execute(update, model)
- r.save(model)
- Ok
- }
- case PostRDF(diffModel) => {
- logger.info("RDF content:\n" + diffModel.toString())
- val model = r.get()
- model.add(diffModel)
- r.save(model)
- Ok
- }
- case PostQuery(query) => {
- logger.info("SPARQL Query:\n" + query.toString())
- lazy val encoding = RDFEncoding(req)
- val model:Model = r.get()
- 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(encoding.toContentType) ~> ResponseModel(model, baseURI, encoding)
- }
- case DESCRIBE => {
- val result:Model = qe.execDescribe()
- Ok ~> ContentType(encoding.toContentType) ~> ResponseModel(model, baseURI, encoding)
- }
- }
- }
- }
- } catch {
- case fnfe:FileNotFoundException => NotFound
- case t:Throwable => InternalServerError ~> ResponseString(t.getStackTraceString)
- }
+ }
case _ => MethodNotAllowed ~> Allow("GET", "PUT", "POST")
}
}
--- a/src/main/scala/Post.scala Wed Aug 31 09:49:01 2011 -0400
+++ b/src/main/scala/Post.scala Sun Sep 04 15:07:07 2011 -0400
@@ -18,8 +18,13 @@
case class PostQuery(query:Query) extends Post
case object PostUnknown extends Post
+import scalaz._
+import Scalaz._
+
object Post {
+ val logger:Logger = LoggerFactory.getLogger(this.getClass)
+
def parse(is:InputStream, baseURI:String):Post = {
val source = Source.fromInputStream(is, "UTF-8")
val s = source.getLines.mkString("\n")
@@ -27,26 +32,25 @@
}
def parse(s:String, baseURI:String):Post = {
+ logger.debug("~~~\n" + s + "\n~~~")
val reader = new StringReader(s)
- try {
+ def postUpdate =
try {
val update:UpdateRequest = UpdateFactory.create(s, baseURI)
- PostUpdate(update)
+ PostUpdate(update).success
} catch {
- case qpe:QueryParseException =>
- try {
- val model = modelFromString(s, baseURI)
- PostRDF(model)
- } catch {
- case je:JenaException => {
- val query = QueryFactory.create(s)
- PostQuery(query)
- }
- }
+ case qpe:QueryParseException => qpe.fail
}
- } catch {
- case _ => PostUnknown
- }
+ def postRDF =
+ modelFromString(s, baseURI) flatMap { model => PostRDF(model).success }
+ def postQuery =
+ try {
+ val query = QueryFactory.create(s)
+ PostQuery(query).success
+ } catch {
+ case qe:QueryException => qe.fail
+ }
+ postUpdate | (postRDF | (postQuery | PostUnknown))
}
}
--- a/src/main/scala/Resource.scala Wed Aug 31 09:49:01 2011 -0400
+++ b/src/main/scala/Resource.scala Sun Sep 04 15:07:07 2011 -0400
@@ -10,14 +10,19 @@
import org.w3.readwriteweb.util._
+import scalaz._
+import Scalaz._
+
+import _root_.scala.sys.error
+
trait ResourceManager {
def basePath:String
def sanityCheck():Boolean
def resource(url:URL):Resource
}
trait Resource {
- def get():Model
- def save(model:Model):Unit
+ def get():Validation[Throwable, Model]
+ def save(model:Model):Validation[Throwable, Unit]
}
class Filesystem(baseDirectory:File, val basePath:String, val lang:String = "RDF/XML-ABBREV")(implicit mode:RWWMode) extends ResourceManager {
@@ -38,33 +43,36 @@
logger.debug("Create file %s with success: %s" format (fileOnDisk.getAbsolutePath, r.toString))
}
- def get():Model = {
+ def get():Validation[Throwable, Model] = {
val m = ModelFactory.createDefaultModel()
if (fileOnDisk.exists()) {
val fis = new FileInputStream(fileOnDisk)
try {
m.read(fis, url.toString)
} catch {
- case je:JenaException => sys.error("File %s was either empty or corrupted: considered as empty graph" format fileOnDisk.getAbsolutePath)
+ case je:JenaException => error("File %s was either empty or corrupted: considered as empty graph" format fileOnDisk.getAbsolutePath)
}
fis.close()
- m
+ m.success
} else {
mode match {
- case AllResourcesAlreadyExist => m
- case ResourcesDontExistByDefault => throw new FileNotFoundException
+ case AllResourcesAlreadyExist => m.success
+ case ResourcesDontExistByDefault => new FileNotFoundException().fail
}
}
}
- def save(model:Model):Unit = {
- createFileOnDisk()
- val fos = new FileOutputStream(fileOnDisk)
- val writer = model.getWriter("RDF/XML-ABBREV")
- writer.write(model, fos, url.toString)
- fos.close()
- }
-
+ def save(model:Model):Validation[Throwable, Unit] =
+ try {
+ createFileOnDisk()
+ val fos = new FileOutputStream(fileOnDisk)
+ val writer = model.getWriter("RDF/XML-ABBREV")
+ writer.write(model, fos, url.toString)
+ fos.close().success
+ } catch {
+ case t => t.fail
+ }
+
}
}
--- a/src/main/scala/util.scala Wed Aug 31 09:49:01 2011 -0400
+++ b/src/main/scala/util.scala Sun Sep 04 15:07:07 2011 -0400
@@ -9,6 +9,11 @@
import java.io._
import scala.io.Source
+import scalaz._
+import Scalaz._
+
+import _root_.scala.sys.error
+
import org.slf4j.{Logger, LoggerFactory}
import com.hp.hpl.jena.rdf.model._
@@ -49,6 +54,11 @@
}
+trait ValidationW[E, S] {
+ val validation:Validation[E, S]
+ def failMap[EE](f:E => EE):Validation[EE, S] = validation.fail map f validation
+}
+
package object util {
val defaultLang = "RDF/XML-ABBREV"
@@ -78,21 +88,38 @@
}
}
- def modelFromInputStream(is:InputStream, base:String, lang:String = "RDF/XML-ABBREV"):Model = {
- val m = ModelFactory.createDefaultModel()
- m.read(is, base, lang)
- m
- }
+ def modelFromInputStream(
+ is:InputStream,
+ base:String,
+ lang:String = "RDF/XML-ABBREV"):Validation[Throwable, Model] =
+ try {
+ val m = ModelFactory.createDefaultModel()
+ m.read(is, base, lang)
+ m.success
+ } catch {
+ case t => t.fail
+ }
- def modelFromString(s:String, base:String, lang:String = "RDF/XML-ABBREV"):Model = {
- val reader = new StringReader(s)
- val m = ModelFactory.createDefaultModel()
- m.read(reader, base, lang)
- m
- }
+ def modelFromString(s:String,
+ base:String,
+ lang:String = "RDF/XML-ABBREV"):Validation[Throwable, Model] =
+ try {
+ val reader = new StringReader(s)
+ val m = ModelFactory.createDefaultModel()
+ m.read(reader, base, lang)
+ m.success
+ } catch {
+ case t => t.fail
+ }
+ implicit def wrapValidation[E, S](v:Validation[E,S]):ValidationW[E, S] =
+ new ValidationW[E, S] { val validation = v }
+
+// implicit def unwrap[E](v:Validation[E,E]):E = v.fold(e => e, s => s)
+
}
+
import java.io.{File, FileWriter}
import java.util.jar._
import scala.collection.JavaConversions._
@@ -100,7 +127,6 @@
import java.net.{URL, URLDecoder}
import org.slf4j.{Logger, LoggerFactory}
-
/** useful stuff to read resources from the classpath */
object MyResourceManager {
@@ -132,7 +158,7 @@
} }
entries filterNot { _.isEmpty } toList
} else
- sys.error("Cannot list files for URL "+dirURL);
+ error("Cannot list files for URL "+dirURL);
}
}
--- a/src/test/scala/Test.scala Wed Aug 31 09:49:01 2011 -0400
+++ b/src/test/scala/Test.scala Sun Sep 04 15:07:07 2011 -0400
@@ -50,7 +50,7 @@
</rdf:RDF>
"""
- val initialModel = modelFromString(joeRDF, joeBaseURI)
+ val initialModel = modelFromString(joeRDF, joeBaseURI).toOption.get
"a GET on a URL that does not exist" should {
"return a 404" in {
@@ -127,7 +127,7 @@
</rdf:RDF>
"""
- val expectedFinalModel = modelFromString(finalRDF, joeBaseURI)
+ val expectedFinalModel = modelFromString(finalRDF, joeBaseURI).toOption.get
"POSTing an RDF document to Joe's URI" should {
"succeed" in {
--- a/src/test/scala/utiltest.scala Wed Aug 31 09:49:01 2011 -0400
+++ b/src/test/scala/utiltest.scala Sun Sep 04 15:07:07 2011 -0400
@@ -40,7 +40,7 @@
class RequestW(req:Request) {
def as_model(base:String, lang:String = "RDF/XML-ABBREV"):Handler[Model] =
- req >> { is => modelFromInputStream(is, base, lang) }
+ req >> { is => modelFromInputStream(is, base, lang).toOption.get }
def post(body:String):Request =
(req <<< body).copy(method="POST")