merged changes into main webid branch. (getting used to hg) webid
authorHenry Story <henry.story@bblfish.net>
Wed, 11 Jan 2012 13:30:56 +0100
branchwebid
changeset 172 9463e0f50340
parent 171 61270101fa04 (current diff)
parent 170 20912130904e (diff)
child 173 fb4c0673ed8f
merged changes into main webid branch. (getting used to hg)
src/main/scala/WebCache.scala
--- a/project/build.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/project/build.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -22,21 +22,19 @@
         <exclude org="net.databinder" module="dispatch-mime_2.9.0-1"/>
       </dependency>
     </dependencies>
-  val slf4jSimple = "org.slf4j" % "slf4j-simple" % "1.6.4"
-  val slf4japi = "org.slf4j" % "slf4j-api" % "1.6.4"
-  val slf4jlog = "org.slf4j" % "slf4j-log4j12" % "1.6.4"
   val antiXML = "com.codecommit" %% "anti-xml" % "0.4-SNAPSHOT" % "test"
-  val jena = "com.hp.hpl.jena" % "jena" % "2.6.4"
+  val jena = "org.apache.jena" % "jena-core" % "2.7.0-incubating"
+  val arq = "org.apache.jena" % "jena-arq" % "2.9.0-incubating"
   val rdfa = "net.rootdev" % "java-rdfa" % "0.4.2-RC2"
   val htmlparser = "nu.validator.htmlparser" % "htmlparser" % "1.2.1"
-  val arq = "com.hp.hpl.jena" % "arq" % "2.8.8"
   val grizzled = "org.clapper" %% "grizzled-scala" % "1.0.8" % "test"
   val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.3"
   val argot =  "org.clapper" %% "argot" % "0.3.5"
-  val guava =  "com.google.guava" % "guava" % "10.0.1"
+  val guava =  "com.google.guava" % "guava" % "11.0"
 //  val restlet = "org.restlet.dev" % "org.restlet" % "2.1-SNAPSHOT"
 //  val restlet_ssl = "org.restlet.dev" % "org.restlet.ext.ssl" % "2.1-SNAPSHOT"
   val jsslutils = "org.jsslutils" % "jsslutils" % "1.0.5"
+  val slf4s = "com.weiglewilczek.slf4s" %% "slf4s" % "1.0.7"
 }
 
 // some usefull repositories
@@ -111,9 +109,6 @@
       libraryDependencies += unfiltered_filter,
       libraryDependencies += unfiltered_jetty,
       libraryDependencies += unfiltered_netty,
-      libraryDependencies += slf4jSimple,
-      libraryDependencies += slf4japi,
-      libraryDependencies += slf4jlog,
 
       libraryDependencies += jena,
       libraryDependencies += arq,
@@ -126,6 +121,7 @@
       libraryDependencies += scalate,
       libraryDependencies += rdfa,
       libraryDependencies += htmlparser,
+      libraryDependencies += slf4s,
 
       jarName in assembly := "read-write-web.jar",
       mainClass in assembly := Some("org.w3.readwriteweb.netty.ReadWriteWebNetty")
--- a/src/main/resources/log4j.properties	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/resources/log4j.properties	Wed Jan 11 13:30:56 2012 +0100
@@ -1,9 +1,9 @@
-// Appenders
-log4j.appender.Console=org.apache.log4j.ConsoleAppender
-log4j.appender.Console.layout=org.apache.log4j.PatternLayout
-// Pattern not for use in production ( %C
-log4j.appender.Console.layout.ConversionPattern=%-5p (%F:%L) : %m%n
-
-// Loggers
-log4j.rootLogger=info, Console
-log4j.logger.org.w3.readwriteweb=debug
+// Appenders
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.layout=org.apache.log4j.PatternLayout
+// Pattern not for use in production ( %C
+log4j.appender.Console.layout.ConversionPattern=%-5p (%F:%L) : %m%n
+
+// Loggers
+log4j.rootLogger=info, Console
+log4j.logger.org.w3.readwriteweb=debug
--- a/src/main/scala/Filesystem.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/Filesystem.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -45,7 +45,7 @@
       logger.debug("%s successfully created: %s" format (fileOnDisk.getAbsolutePath, r.toString))
     }
     
-    def get(): Validation[Throwable, Model] = {
+    def get(unused: CacheControl.Value = CacheControl.CacheFirst): Validation[Throwable, Model] = {
       val model = ModelFactory.createDefaultModel()
       val guessLang = fileOnDisk.getName match {
         case Authoritative.r(_,suffix) => Representation.fromSuffix(suffix) match {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/GraphCache.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -0,0 +1,140 @@
+ /*
+ * Copyright (c) 2011 Henry Story (bblfish.net)
+ * under the MIT licence defined
+ *    http://www.opensource.org/licenses/mit-license.html
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in the
+ * Software without restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */ 
+
+package org.w3.readwriteweb
+
+import com.hp.hpl.jena.rdf.model.Model
+import org.apache.http.MethodNotSupportedException
+import org.w3.readwriteweb.util._
+import java.net.{ConnectException, URL}
+import scalaz.{Scalaz, Validation}
+import java.util.concurrent.TimeUnit
+import com.google.common.cache.{LoadingCache, CacheLoader, CacheBuilder, Cache}
+import java.io.{File, FileOutputStream}
+import com.weiglewilczek.slf4s.Logging
+
+
+/**
+ * Fetch resources on the Web and cache them
+ * ( at a later point this would include saving them to an indexed quad store )
+ *
+ * @author Henry Story
+ * @created: 12/10/2011
+ *
+ */
+object GraphCache extends ResourceManager with Logging {
+  import dispatch._
+  import Scalaz._
+
+  //use shellac's rdfa parser
+  new net.rootdev.javardfa.jena.RDFaReader  //import rdfa parser
+
+
+  //this is a simple but quite stupid web cache so that graphs can stay in memory and be used a little
+  // bit across sessions
+  val cache: LoadingCache[URL,Validation[Throwable,Model]] =
+       CacheBuilder.newBuilder()
+         .expireAfterAccess(5, TimeUnit.MINUTES)
+         .softValues()
+//         .expireAfterWrite(30, TimeUnit.MINUTES)
+       .build(new CacheLoader[URL, Validation[Throwable,Model]] {
+         def load(url: URL) = getUrl(url)
+       })
+
+  val http = new Http with thread.Safety {
+    import org.apache.http.params.CoreConnectionPNames
+    client.getParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 3000)
+    client.getParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, 15000)
+  }
+  
+  def basePath = null //should be cache dir?
+
+  def sanityCheck() = true  //cache dire exists? But is this needed for functioning?
+
+  def resource(u : URL) = new org.w3.readwriteweb.Resource {
+    import CacheControl._
+    def name() = u
+    def get(cacheControl: CacheControl.Value) = cacheControl match {
+      case CacheOnly => {
+        val res = cache.getIfPresent(u)
+        if (null==res) NoCachEntry.fail
+        else res
+      }
+      case CacheFirst => cache.get(u)
+      case NoCache => {
+        val res = getUrl(u)
+        cache.put(u,res) //todo: should this only be done if say the returned value is not an error?
+        res
+      }
+    }
+    // when fetching information from the web creating directories does not make sense
+    //perhaps the resource manager should be split into read/write sections?
+    def save(model: Model) =  throw new MethodNotSupportedException("not implemented")
+
+    def createDirectory(model: Model) =  throw new MethodNotSupportedException("not implemented")
+  }
+
+  private def getUrl(u: URL) = {
+
+      // note we prefer rdf/xml and turtle over html, as html does not always contain rdfa, and we prefer those over n3,
+      // as we don't have a full n3 parser. Better would be to have a list of available parsers for whatever rdf framework is
+      // installed (some claim to do n3 when they only really do turtle)
+      // we can't currently accept */* as we don't have GRDDL implemented
+      val request = url(u.toString) <:< Map("Accept"->
+        "application/rdf+xml,text/turtle,application/xhtml+xml;q=0.8,text/html;q=0.7,text/n3;q=0.6")
+      logger.info("fetching "+u.toExternalForm)
+
+      //we need to tell the model about the content type
+      val handler: Handler[Validation[Throwable, Model]] = request.>+>[Validation[Throwable, Model]](res =>  {
+        res >:> { headers =>
+          val encoding = headers("Content-Type").headOption match {
+            case Some(mime) => {
+              Lang(mime.split(";")(0)) getOrElse Lang.default
+            }
+            case None => RDFXML  //todo: it would be better to try to do a bit of guessing in this case by looking at content
+          }
+          val loc = headers("Content-Location").headOption match {
+            case Some(loc) =>  new URL(u,loc)
+            case None => new URL(u.getProtocol,u.getAuthority,u.getPort,u.getPath)
+          }
+          res>>{ in=> modelFromInputStream(in,loc,encoding) }
+
+        }
+      })
+      try {
+        val future = http(handler)
+        future
+      } catch {
+        case e: ConnectException => {
+          logger.info("failed to connect to "+u.getHost,e)
+          e.fail
+        }
+      }
+
+    }
+
+
+   override def finalize() { http.shutdown() }
+}
+
+ object NoCachEntry extends Exception
\ No newline at end of file
--- a/src/main/scala/Lang.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/Lang.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -59,7 +59,7 @@
       case "text/turtle" => Some(TURTLE)
       case "application/rdf+xml" => Some(RDFXML)
       case "text/html" => Some(HTML)
-      case "text/xhtml" => Some(XHTML)
+      case "application/xhtml+xml" => Some(XHTML)
       case _ => None
   }    
   
--- a/src/main/scala/ReadWriteWebMain.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/ReadWriteWebMain.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -5,18 +5,20 @@
 import org.w3.readwriteweb.util._
 
 import unfiltered.jetty._
-import java.io.File
 import Console.err
 import org.slf4j.{Logger, LoggerFactory}
 
 import org.clapper.argot._
 import ArgotConverters._
 import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
-import java.security.KeyStore
+import com.weiglewilczek.slf4s.Logging
+import java.lang.{Class, String}
+import java.net.InetAddress
+import java.io.{FileDescriptor, File}
+import java.security.{Permission, KeyStore}
 
 trait ReadWriteWebArgs {
   val logger: Logger = LoggerFactory.getLogger(this.getClass)
-  new net.rootdev.javardfa.jena.RDFaReader  //import rdfa parser
 
   val postUsageMsg= Some("""
   |PROPERTIES
@@ -79,6 +81,7 @@
 }
 
 
+
 object ReadWriteWebMain extends ReadWriteWebArgs {
 
   import unfiltered.filter.Planify
@@ -92,6 +95,7 @@
       case e: ArgotUsageException => err.println(e.message); sys.exit(1)
     }
 
+
     val filesystem =
       new Filesystem(
         rootDirectory.value.get,
--- a/src/main/scala/Resource.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/Resource.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -13,9 +13,13 @@
   def resource(url:URL):Resource
 }
 
+object CacheControl extends Enumeration {
+  val CacheOnly, CacheFirst, NoCache = Value
+}
+
 trait Resource {
   def name: URL
-  def get():Validation[Throwable, Model]
+  def get(policy: CacheControl.Value = CacheControl.CacheFirst): Validation[Throwable, Model]
   def save(model:Model):Validation[Throwable, Unit]
   def createDirectory(model: Model): Validation[Throwable, Unit]
 }
--- a/src/main/scala/WebCache.scala	Wed Jan 11 13:30:13 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
- /*
- * Copyright (c) 2011 Henry Story (bblfish.net)
- * under the MIT licence defined
- *    http://www.opensource.org/licenses/mit-license.html
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in the
- * Software without restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */ 
-
-package org.w3.readwriteweb
-
-import com.hp.hpl.jena.rdf.model.Model
-import org.apache.http.MethodNotSupportedException
-import org.w3.readwriteweb.util._
-import java.net.{ConnectException, URL}
-import scalaz.{Scalaz, Validation}
-import java.util.concurrent.TimeUnit
-import com.google.common.cache.{CacheLoader, CacheBuilder, Cache}
-
-
-/**
- * @author Henry Story
- * @created: 12/10/2011
- *
- * The WebCache currently does not cache
- */
-object WebCache extends ResourceManager  {
-  import dispatch._
-  import Scalaz._
-
-  //this is a simple but quite stupid web cache so that graphs can stay in memory and be used a little
-  // bit across sessions
-  val cache: Cache[URL,Validation[Throwable,Model]] =
-       CacheBuilder.newBuilder()
-         .expireAfterAccess(5, TimeUnit.MINUTES)
-//         .softValues()
-//         .expireAfterWrite(30, TimeUnit.MINUTES)
-       .build(new CacheLoader[URL, Validation[Throwable,Model]] {
-         def load(url: URL) = getUrl(url)
-       })
-
-  val http = new Http with thread.Safety
-  
-  def basePath = null //should be cache dir?
-
-  def sanityCheck() = true  //cache dire exists? But is this needed for functioning?
-
-  def resource(u : URL) = new org.w3.readwriteweb.Resource {
-    def name() = u
-    def get() = cache.get(u)
-
-    // when fetching information from the web creating directories does not make sense
-    //perhaps the resource manager should be split into read/write sections?
-    def save(model: Model) =  throw new MethodNotSupportedException("not implemented")
-
-    def createDirectory(model: Model) =  throw new MethodNotSupportedException("not implemented")
-  }
-
-  private def getUrl(u: URL) = {
-
-      // note we prefer rdf/xml and turtle over html, as html does not always contain rdfa, and we prefer those over n3,
-      // as we don't have a full n3 parser. Better would be to have a list of available parsers for whatever rdf framework is
-      // installed (some claim to do n3 when they only really do turtle)
-      // we can't currently accept */* as we don't have GRDDL implemented
-      val request = url(u.toString) <:< Map("Accept"->
-        "application/rdf+xml,text/turtle,application/xhtml+xml;q=0.8,text/html;q=0.7,text/n3;q=0.6")
-
-      //we need to tell the model about the content type
-      val handler: Handler[Validation[Throwable, Model]] = request.>+>[Validation[Throwable, Model]](res =>  {
-        res >:> { headers =>
-          val encoding = headers("Content-Type").headOption match {
-            case Some(mime) => {
-              Lang(mime.split(";")(0)) getOrElse Lang.default
-            }
-            case None => RDFXML  //todo: it would be better to try to do a bit of guessing in this case by looking at content
-          }
-          val loc = headers("Content-Location").headOption match {
-            case Some(loc) =>  new URL(u,loc)
-            case None => new URL(u.getProtocol,u.getAuthority,u.getPort,u.getPath)
-          }
-          res>>{ in=> modelFromInputStream(in,loc,encoding) }
-
-        }
-      })
-      try {
-        val future = http(handler)
-        future
-      } catch {
-        case e: ConnectException => e.fail
-      }
-
-    }
-
-
-   override def finalize() { http.shutdown() }
-}
--- a/src/main/scala/auth/Authz.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/Authz.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -30,7 +30,7 @@
 import com.hp.hpl.jena.query.{QueryExecutionFactory, QuerySolutionMap, QueryFactory}
 import unfiltered.response.{ResponseFunction, Unauthorized}
 import com.hp.hpl.jena.rdf.model.ResourceFactory
-import org.w3.readwriteweb.{Authoritative, Resource, ResourceManager, WebCache}
+import org.w3.readwriteweb.{Authoritative, Resource, ResourceManager, GraphCache}
 import org.w3.readwriteweb.util.HttpMethod
 
 /**
--- a/src/main/scala/auth/Principals.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/Principals.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -24,12 +24,12 @@
 package org.w3.readwriteweb.auth
 
 import java.security.Principal
-import org.w3.readwriteweb.WebCache
 import com.hp.hpl.jena.rdf.model.Model
 import com.hp.hpl.jena.shared.WrappedIOException
 import scalaz.{Scalaz, Validation}
 import Scalaz._
 import java.net.{ConnectException, URL}
+import org.w3.readwriteweb.{CacheControl, GraphCache}
 
 /**
  * @author Henry Story from http://bblfish.net/
@@ -75,10 +75,10 @@
     case _ => false
   }
 
-  //TODO: now that we are no longer passing the WebCache around it's questionable whether we still need this method
+  //TODO: now that we are no longer passing the GraphCache around it's questionable whether we still need this method
   //in this class
-  def getDefiningModel: Validation[ProfileError, Model] =
-    WebCache.resource(url).get() failMap {
+  def getDefiningModel(cacheControl: CacheControl.Value = CacheControl.CacheFirst): Validation[ProfileError, Model] =
+    GraphCache.resource(url).get(cacheControl) failMap {
       case ioe: WrappedIOException => new ProfileGetError("error fetching profile", Some(ioe),url)
       case connE : ConnectException => new ProfileGetError("error fetching profile", Some(connE),url)
       case other => new ProfileParseError("error parsing profile", Some(other),url)
--- a/src/main/scala/auth/WebIDSrvc.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/WebIDSrvc.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -33,7 +33,6 @@
 import com.hp.hpl.jena.rdf.model.ModelFactory
 import sommer.{CertAgent, Extractors}
 import org.w3.readwriteweb.util._
-import org.w3.readwriteweb.auth._
 import unfiltered.request._
 import org.fusesource.scalate.scuery.{Transform, Transformer}
 import org.w3.readwriteweb.netty.ReadWriteWebNetty.StaticFiles
@@ -41,6 +40,7 @@
 import xml._
 import unfiltered.response._
 import java.security.interfaces.RSAPublicKey
+import org.w3.readwriteweb.auth._
 
 object WebIDSrvc {
   val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
@@ -86,16 +86,23 @@
       if (next!=Nil && next.size==1) srvStaticFiles(next.head)
       else req match {
         case Params(RelyingParty(rp)) => req match {
-          case Params(DoIt(_)) & XClaim(claim: XClaim) => {
-            val answer = if (claim == NoClaim) "error=nocert&"
-            else if (claim.claims.size == 0) "error=noWebID&"
-            else if (claim.verified.size == 0) "error=noVerifiedWebID&" +
-              claim.claims.map(claim => claim.verify.failMap(e => "cause="+e.getMessage)).mkString("&")+"&"
-            else claim.verified.slice(0, 3).foldRight("") {
-              (wid, str) => "webid=" + URLEncoder.encode(wid.url.toExternalForm, "UTF-8") + "&"
+          //&doit => we redirect to the relying party with the identity answer
+          //TODO: here I would have liked to go with what the user decided without forcing him to login,
+          //ie by using XClaim(claim) in the match,
+          //but http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
+          case Params(DoIt(_))  => {
+            val answer = X509Claim.unapply(req) match {
+              case None => "error=nocert&"
+              case Some(claim) =>
+                if (claim.claims.size == 0) "error=noWebID&"
+                else if (claim.verified.size == 0) "error=noVerifiedWebID&" +
+                  claim.claims.map(claim => claim.verify.failMap(e => "cause=" + e.getMessage)).mkString("&") + "&"
+                else claim.verified.slice(0, 3).foldRight("") {
+                  (wid, str) => "webid=" + URLEncoder.encode(wid.url.toExternalForm, "UTF-8") + "&"
+                }
             }
             val signedAnswer = sign(rp.toExternalForm + "?" + answer).toExternalForm
-            Redirect(signedAnswer)
+            Redirect(signedAnswer) ~> Expires("0") ~> CacheControl("no-cache")
           }
           //GET=>The user just arrived on the page. We recuperated the X509 claim in case he has authenticated already
           case GET(_) & XClaim(claim: XClaim) => {
@@ -103,7 +110,7 @@
               case NoClaim => profilePg
               case claim: X509Claim => if (claim.verified.size > 0) authenticatedPg else errorPg
             }
-            Ok ~> Html5(new ServiceTrans(rp, claim).apply(pg))
+            Ok ~> Html5(new ServiceTrans(rp, claim).apply(pg)) ~> Expires("0") ~> CacheControl("no-cache")
           }
           //POST=> we authenticate the user because he has agreed to be authenticated on the page, which we know if the
           // request is a POST
@@ -159,7 +166,7 @@
           claim match {
             case NoClaim => <span/>
             case _ => new Transform(node) {
-              val union = claim.verified.flatMap(_.getDefiningModel.toOption).fold(ModelFactory.createDefaultModel()) {
+              val union = claim.verified.flatMap(_.getDefiningModel().toOption).fold(ModelFactory.createDefaultModel()) {
                 (m1, m2) => m1.add(m2)
               }
               //this works because we have verified before
--- a/src/main/scala/auth/WebIdClaim.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/WebIdClaim.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -32,6 +32,8 @@
 import com.hp.hpl.jena.query._
 import java.math.BigInteger
 import com.hp.hpl.jena.datatypes.xsd.XSDDatatype
+import org.w3.readwriteweb.CacheControl
+import scalaz.{Failure, Scalaz, Validation}
 
 
 /**
@@ -95,9 +97,15 @@
       }
   }
 
-  lazy val verify: Validation[WebIDClaimFailure, WebID] = key match {
+  def verify: Validation[WebIDClaimFailure, WebID] = key match {
       case rsakey: RSAPublicKey =>
-        WebID(san).flatMap(webid=> webid.getDefiningModel.flatMap(rsaTest(webid, rsakey)) )
+        WebID(san).flatMap(webid=> {
+          webid.getDefiningModel(CacheControl.CacheOnly).flatMap(rsaTest(webid, rsakey)) match {
+            case Failure(_) => webid.getDefiningModel(CacheControl.NoCache).flatMap(rsaTest(webid, rsakey))
+            case o => o
+          }
+        }
+        )
       case _ => new UnsupportedKeyType("We only support RSA keys at present", key).fail
   }
 }
--- a/src/main/scala/auth/X509Cert.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/X509Cert.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -217,9 +217,11 @@
     val sslh = r.underlying.context.getPipeline.get(classOf[SslHandler])
     
     trySome(sslh.getEngine.getSession.getPeerCertificates.toIndexedSeq) orElse {
+      //it seems that the jvm does not keep a very good cache of remote certificates in a session. But
+      //see http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
       if (!fetch) None
       else {
-        sslh.setEnableRenegotiation(true)
+        sslh.setEnableRenegotiation(true) // todo: does this have to be done on every request?
         r match {
           case UserAgent(agent) if needAuth(agent) => sslh.getEngine.setNeedClientAuth(true)
           case _ => sslh.getEngine.setWantClientAuth(true)
--- a/src/main/scala/auth/X509Claim.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/X509Claim.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -32,10 +32,11 @@
 import unfiltered.request.HttpRequest
 import java.security.interfaces.RSAPublicKey
 import collection.immutable.List
-import com.google.common.cache.{CacheLoader, CacheBuilder, Cache}
 import java.util.concurrent.TimeUnit
 import org.w3.readwriteweb.util.trySome
 import java.util.Date
+import com.weiglewilczek.slf4s.Logging
+import com.google.common.cache.{LoadingCache, CacheLoader, CacheBuilder, Cache}
 
 /**
  * @author hjs
@@ -47,8 +48,8 @@
   implicit val fetch = true //fetch the certificate if we don't have it
 
 // this is cool because it is not in danger of running out of memory but it makes it impossible to create the claim
-// with an implicit  WebCache...
-  val idCache: Cache[X509Certificate, X509Claim] =
+// with an implicit  GraphCache...
+  val idCache: LoadingCache[X509Certificate, X509Claim] =
      CacheBuilder.newBuilder()
      .expireAfterWrite(30, TimeUnit.MINUTES)
      .build(new CacheLoader[X509Certificate, X509Claim] {
@@ -92,6 +93,8 @@
 }
 
 object XClaim {
+  //warning: it turns out that one nearly never recuperates the client certificate
+  //see http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
   implicit val fetch = false
   def unapply[T](r: HttpRequest[T])(implicit m: Manifest[T]): Option[XClaim] = r match {
     case Certs(c1: X509Certificate, _*) => trySome(X509Claim.idCache.get(c1))
@@ -122,19 +125,19 @@
  * @created: 30/03/2011
  */
 // can't be a case class as it then creates object which clashes with defined one
-class X509Claim(val cert: X509Certificate) extends Refreshable with XClaim {
+class X509Claim(val cert: X509Certificate) extends Refreshable with XClaim with Logging {
 
   import X509Claim._
   val claimReceivedDate = new Date()
   lazy val tooLate = claimReceivedDate.after(cert.getNotAfter())
   lazy val tooEarly = claimReceivedDate.before(cert.getNotBefore())
-
+  logger.debug("X509 Claim for certificate with DN="+cert.getSubjectDN)
 
   lazy val claims: List[WebIDClaim] = getClaimedWebIds(cert) map { webid => 
     new WebIDClaim(webid, cert.getPublicKey.asInstanceOf[RSAPublicKey]) 
   }
 
-  lazy val verified: List[WebID] = claims.flatMap(_.verify.toOption)
+  def verified: List[WebID] = claims.flatMap(_.verify.toOption)
 
   //note could also implement Destroyable
   //
--- a/src/main/scala/auth/X509view.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/X509view.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -23,17 +23,14 @@
 
 package org.w3.readwriteweb.auth
 
-import java.io.File
-import unfiltered.response.{Ok, Html}
 import unfiltered.Cycle
-import org.fusesource.scalate.{Binding, TemplateEngine}
 import xml.{Elem, XML}
 import unfiltered.request.Path
 import org.fusesource.scalate.scuery.{Transform, Transformer}
-import org.w3.readwriteweb.WebCache
 import unfiltered.scalate.Scalate
 import java.text.DateFormat
 import java.util.Date
+import unfiltered.response.{CacheControl, Expires, Ok, Html}
 
 /**
  * This plan just described the X509 WebID authentication information.
@@ -56,8 +53,8 @@
   
   def intent : Cycle.Intent[Req,Res] = {
     case req @ Path("/test/WebId")  => req match {
-      case X509Claim(claim) => Ok ~> Html( new X509Filler(claim).apply(webidTst) )
-      case _ => Ok ~> Html (new NoX509().apply(noX509))
+      case X509Claim(claim) => Ok ~> Html( new X509Filler(claim).apply(webidTst) ) ~> Expires("0") ~> CacheControl("no-cache")
+      case _ => Ok ~> Html (new NoX509().apply(noX509)) ~> Expires("0") ~> CacheControl("no-cache")
     }
     case req @ Path("/test/WebIdAuth2") => Ok ~> Scalate(req, "hello.ssp")
   }
--- a/src/main/scala/netty/ReadWriteWebNetty.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/netty/ReadWriteWebNetty.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -23,6 +23,7 @@
 
 package org.w3.readwriteweb.netty
 
+
 import _root_.auth.WebIDSrvc
 import org.clapper.argot.ArgotUsageException
 import scala.Console._
@@ -34,7 +35,7 @@
 import unfiltered.response._
 import unfiltered.netty._
 import collection.immutable.List
-import java.lang.String
+import util.{NetworkLoggingSM, AllowAllSecurityManager}
 
 /**
  * ReadWrite Web for Netty server, allowing TLS renegotiation
@@ -72,6 +73,7 @@
        case None => new KeyAuth_Https(httpPort.value.get)
      }
 
+
      // configures and launches a Netty server
      service.plan(publicStatic).
        plan( x509v ).
@@ -81,6 +83,7 @@
    }
 
 
+
   trait StaticFiles extends PartialFunction[String, ResponseFunction[Any]] {
     /* override this if the local path is somehow different from the url path */
     def toLocal(webpath: String): String = webpath
--- a/src/main/scala/sommer/ResourceReader.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/sommer/ResourceReader.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -26,7 +26,7 @@
 import com.hp.hpl.jena.vocabulary.RDF
 import com.hp.hpl.jena.sparql.vocabulary.FOAF
 import java.lang.String
-import org.w3.readwriteweb.{Resource, WebCache}
+import org.w3.readwriteweb.{Resource, GraphCache}
 import scalaz.Validation
 import java.net.URL
 import collection._
@@ -107,7 +107,7 @@
   type Val[A] = Validation[scala.Throwable,A]
  
   def findPeople(m: Resource): Validation[scala.Throwable,Set[Person]] = {
-     for (gr<-m.get) yield {
+     for (gr<-m.get()) yield {
        for (st <- gr.listStatements(null,RDF.`type`,FOAF.Person).asScala;
         val subj = st.getSubject;
         st2 <- gr.listStatements(subj, FOAF.name,null).asScala
@@ -137,13 +137,13 @@
   }
 
   def findDefinedPeople(m: Resource): Validation[scala.Throwable,Set[IdPerson]] = {
-    for (gr<-m.get) yield {
+    for (gr<-m.get()) yield {
       definedPeople(gr, m.name)
     }.toSet
   }
   
   def findIdPeople(m: Resource): Val[Set[IdPerson]] = {
-    for (gr<-m.get) yield {
+    for (gr<-m.get()) yield {
       for (st <- gr.listStatements(null,RDF.`type`,FOAF.Person).asScala;
            val subj = st.getSubject;
            if (subj.isURIResource)
@@ -162,7 +162,7 @@
 }
 
 object Test {
-  implicit def urlToResource(u: URL) = WebCache.resource(u)
+  implicit def urlToResource(u: URL) = GraphCache.resource(u)
   import System._
 
   val peopleRd = new ResourceReader[Set[Person]](Extractors.findPeople)
@@ -170,7 +170,7 @@
   val idPeopleRd = new ResourceReader[Set[IdPerson]](Extractors.findIdPeople)
   val definedPeopleFriends = definedPeopleRd.flatMap(people =>ResourceReader[Set[IdPerson]]{
     resource: Resource =>
-       resource.get.map(gr=>
+       resource.get().map(gr=>
          for ( p <- people;
                st <- gr.listStatements(p.id, FOAF.knows, null).asScala ;
               val friend = st.getObject;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/util/LogSecurityManager.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2012 Henry Story (bblfish.net)
+ * under the MIT licence defined at
+ *    http://www.opensource.org/licenses/mit-license.html
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of 
+ * this software and associated documentation files (the "Software"), to deal in the
+ * Software without restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package org.w3.readwriteweb.util
+
+import com.weiglewilczek.slf4s.Logging
+import java.lang.{Class, String}
+import java.net.InetAddress
+import java.io.FileDescriptor
+import java.security.Permission
+
+/**
+ * This class can be set as the SecurityManager on the command line with
+ * -Djava.security.manager=org.w3.readwriteweb.util.NetworkLoggingSM
+ * IT will show all attempts to make outbound network connections, which can be useful to track connections made in
+ * various libraries
+ */
+class NetworkLoggingSM extends AllowAllSecurityManager with Logging {
+
+  override def checkConnect(host: String, port: Int) {
+    logger.info("connecting to "+host+":"+port)
+    super.checkConnect(host,port)
+  }
+
+  override def checkConnect(host: String, port: Int, context: AnyRef) {
+    logger.info("connecting to "+host+":"+port)
+    super.checkConnect(host,port,context)
+  }
+    
+}
+
+class AllowAllSecurityManager extends SecurityManager {
+  override def checkAccept(host: String, port: Int) {}
+
+
+  override def checkAwtEventQueueAccess() {}
+
+  override def checkConnect(host: String, port: Int) {}
+
+  override def checkConnect(host: String, port: Int, context: AnyRef) {}
+
+  override def checkCreateClassLoader() {}
+
+  override def checkDelete(file: String) {}
+
+  override def checkExec(cmd: String) {}
+
+  override def checkExit(status: Int) {}
+
+  override def checkLink(lib: String) {}
+
+  override def checkListen(port: Int) {}
+
+  override def checkMemberAccess(clazz: Class[_], which: Int) {}
+
+  override def checkMulticast(maddr: InetAddress) {}
+
+  override def checkMulticast(maddr: InetAddress, ttl: Byte) {}
+
+  override def checkPackageAccess(pkg: String) {}
+
+  override def checkPackageDefinition(pkg: String) {}
+
+  override def checkPermission(perm: Permission) {}
+
+  override def checkPermission(perm: Permission, context: AnyRef) {}
+
+  override def checkPrintJobAccess() {}
+
+  override def checkPropertiesAccess() {}
+
+  override def checkPropertyAccess(key: String) {}
+
+  override def checkRead(fd: FileDescriptor) {}
+
+  override def checkRead(file: String) {}
+
+  override def checkRead(file: String, context: AnyRef) {}
+
+  override def checkSecurityAccess(target: String) {}
+
+  override def checkSetFactory() {}
+
+  override def checkSystemClipboardAccess() {}
+
+  override def checkTopLevelWindow(window: AnyRef) = false
+
+  override def checkWrite(fd: FileDescriptor) {}
+
+  override def checkWrite(file: String) {}
+}
+
+trait WrappedSecurityManager extends SecurityManager  {
+  val wrap: SecurityManager
+  override def getSecurityContext = wrap.getSecurityContext
+
+  override def checkPermission(perm: Permission) { wrap.checkPermission(perm)}
+
+  override def checkPermission(perm: Permission, context: AnyRef) {wrap.checkPermission(perm,context)}
+
+  override def checkCreateClassLoader() { wrap.checkCreateClassLoader() }
+
+  override def checkAccess(t: Thread) { wrap.checkAccess(t)}
+
+  override def checkAccess(g: ThreadGroup) { wrap.checkAccess(g)}
+
+  override def checkExit(status: Int) { wrap.checkExit(status) }
+
+  override def checkExec(cmd: String) { wrap.checkExec(cmd) }
+
+  override def checkLink(lib: String) { wrap.checkLink(lib)}
+
+  override def checkRead(fd: FileDescriptor) {wrap.checkRead(fd)}
+
+  override def checkRead(file: String) { wrap.checkRead(file)}
+
+  override def checkRead(file: String, context: AnyRef) { wrap.checkRead(file)}
+
+  override def checkWrite(fd: FileDescriptor) {wrap.checkWrite(fd)}
+
+  override def checkWrite(file: String) {wrap.checkWrite(file)}
+
+  override def checkDelete(file: String) {wrap.checkDelete(file)}
+
+  override def checkListen(port: Int) {wrap.checkListen(port)}
+
+  override def checkAccept(host: String, port: Int) {wrap.checkAccept(host,port)}
+
+  override def checkMulticast(maddr: InetAddress) { wrap.checkMulticast(maddr)}
+
+  override def checkPropertiesAccess() { wrap.checkPropertiesAccess()}
+
+  override def checkPropertyAccess(key: String) {wrap.checkPropertyAccess(key)}
+
+  override def checkTopLevelWindow(window: AnyRef) = wrap.checkTopLevelWindow(window)
+
+  override def checkPrintJobAccess() { wrap.checkPrintJobAccess()}
+
+  override def checkSystemClipboardAccess() { wrap.checkSystemClipboardAccess()}
+
+  override def checkAwtEventQueueAccess() { wrap.checkAwtEventQueueAccess()}
+
+  override def checkPackageAccess(pkg: String) { wrap.checkPackageAccess(pkg)}
+
+  override def checkPackageDefinition(pkg: String) {wrap.checkPackageDefinition(pkg)}
+
+  override def checkSetFactory() {wrap.checkSetFactory()}
+
+  override def checkMemberAccess(clazz: Class[_], which: Int) {wrap.checkMemberAccess(clazz,which)}
+
+  override def checkSecurityAccess(target: String) {wrap.checkSecurityAccess(target)}
+
+  override def getThreadGroup = wrap.getThreadGroup
+}
+
+
+
+
+
+
--- a/src/main/scala/util/SpyInputStream.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/util/SpyInputStream.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -34,7 +34,7 @@
 
 class SpyInputStream(val in: InputStream, val out: OutputStream) extends InputStream {
   var stopOut = false
-  
+
   def read() ={
 
     val i = try {
--- a/src/main/scala/util/package.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/util/package.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -5,9 +5,9 @@
 import scalaz._
 import Scalaz._
 import java.net.URL
-import com.hp.hpl.jena.shared.WrappedIOException
+import com.weiglewilczek.slf4s.Logging
 
-package object util {
+package object util extends Logging {
   
   def modelFromInputStream(
       is: InputStream,
@@ -18,7 +18,10 @@
       m.getReader(lang.jenaLang).read(m, is, base.toString)
       m.success
     } catch {
-      case t => t.fail
+      case t =>  {
+        logger.info("cought exception turning stream into model ",t)
+        t.fail
+      }
     }
   
   def modelFromString(
--- a/src/test/scala/auth/CreateWebIDSpec.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/test/scala/auth/CreateWebIDSpec.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -39,7 +39,7 @@
 
 /**
  * A key manager that can contain multiple keys, but where the client can take one of a number of identities
- * One at a time - so this is not sychronised. It also assumes that the server will accept all CAs, which in
+ * One at a time - so this is not synchronised. It also assumes that the server will accept all CAs, which in
  * these test cases it does.
  */
 class FlexiKeyManager extends X509ExtendedKeyManager {
--- a/src/test/scala/auth/secure_specs.scala	Wed Jan 11 13:30:13 2012 +0100
+++ b/src/test/scala/auth/secure_specs.scala	Wed Jan 11 13:30:56 2012 +0100
@@ -93,7 +93,7 @@
 
 
 
-  val webCache = WebCache
+  val webCache = GraphCache
   val serverSslContext = javax.net.ssl.SSLContext.getInstance("TLS");