better doc, cmd line info, & netty does http, ... (clean up) webid-java6
authorHenry Story <henry.story@bblfish.net>
Tue, 22 May 2012 23:04:54 +0200
branchwebid-java6
changeset 204 3ed197d09cba
parent 203 0e6fc253828c
child 205 674d9bb48ea7
better doc, cmd line info, & netty does http, ... (clean up)
README.markdown
project/build.scala
src/main/scala/ReadWriteWebArgs.scala
src/main/scala/ReadWriteWebJetty.scala
src/main/scala/ReadWriteWebMain.scala
src/main/scala/auth/WebIDSrvc.scala
src/main/scala/auth/X509Cert.scala
src/main/scala/netty/ReadWriteWebNetty.scala
src/main/scala/netty/SslLoginTest.scala
src/main/scala/sommer/ResourceReader.scala
test_www/.meta.n3
test_www/meta.n3
--- a/README.markdown	Fri May 11 17:27:57 2012 +0200
+++ b/README.markdown	Tue May 22 23:04:54 2012 +0200
@@ -40,7 +40,7 @@
 
 ### to auto-compile the source
 
-    > ~ compile
+    > compile
 
 ### to launch tests under sbt (will cache all the dependencies the first time, can take a while)
 
@@ -48,16 +48,94 @@
 
 ### to run the Web App
 
+Get the full command line options 
+
+    > run --help
+
+You are then given a choise of using either the Jetty or the Netty Server.  ( The Netty Server has better WebID support.)
+You will then get a listing of all the options.
+
+The following is known to work with https
+
+   > run --lang turtle --keyStore src/test/resources/KEYSTORE.jks --ksPass secret --https 8443 test_www /2012/
+
+Because of the .meta.n3 in the test_www directory you will not be able to GET the contents there
+
+
+The ReadWriteWeb app
+--------------------
+
+To get this:
+
+    hg clone https://dvcs.w3.org/hg/read-write-web
+    cd read-write-web
+    less README.markdown
+    ./sbt
+    
+* see [http://mercurial.selenic.com/](http://mercurial.selenic.com/) for hg
+* see [https://github.com/harrah/xsbt/wiki](https://github.com/harrah/xsbt/wiki) for sbt
+
+This project depends on:
+
+* Java 6
+* that's all :-)
+
+It comes with
+
+* sbt project
+* generic sbt launcher
+* jar packager (assembly)
+* eclipse plugin for sbt
+* Web framework (Unfiltered)
+* embedded Web server (Jetty)
+* tests for web api (specs)
+* logger (slf4j)
+* the Jena/ARQ libraries
+
+How to start geeking
+--------------------
+
+BE PATIENT: the first time, some operations take some time because it downloads
+            all the dependencies...
+
+### to launch sbt
+
+    $ ./sbt
+
+### to auto-compile the source
+
+    >  compile
+
+### to launch tests under sbt (will cache all the dependencies the first time, can take a while)
+
+    > test
+
+### to run the Web App and get the full command line options
+
     > run
 
-or
+    Choose either the Netty or the Jetty servers to continue.
+    You will get a full list of options
 
-    > run 8080
+    To start an insecure server start
+   
+    > run --lang turtle  --http 8080 test_www /2012/ 
+
+   You can now request resources using curl on the command line
+
+   $ curl -i -H "Accept: application/rdf+xml" http://localhost:8080/2012/foaf.n3 
+
+   It is possible to PUT, RDF resources too and a couple of image formats, as 
+   well as to create directories etc...
 
 ### to generate the eclipse configuration
 
     > eclipse same-targets
 
+### to generate the IntelliJ configuration
+  
+    > gen-idea
+
 ### to package the application as a stand-alone jar (creates target/read-write-web.jar)
 
     > assembly
@@ -65,39 +143,42 @@
 Using the stand-alone jar
 -------------------------
 
-    java -jar target/read-write-web.jar 8080 ~/WWW/2011/09 /2011/09  [options]
-
-Options:
+    $ java -jar target/read-write-web.jar 
 
- *   --relax   All documents exist as empty RDF files (like a wiki).
- *   --strict  Documents must be created using PUT else they return 404
-
-To run with WebID see next section.
-    
     
 HTTPS with WebID 
 ----------------
 
-### to run on https with WebID
+### to run on https with WebID ( http://webid.info/ )
     
-    Wether you run the binary from the command line as described below or run it directly inside sbt you need to set the following parameters to java
- * -Djetty.ssl.keyStoreType=JKS                              - the keystore type (usually JKS)
- * -Djetty.ssl.keyStore=src/test/resources/KEYSTORE.jks      - the path to the keystore  
- * -Djetty.ssl.keyStorePassword=secret                       - the secret password for the keystore
- * -Dsun.security.ssl.allowUnsafeRenegotiation=true          - to allow unsafe TLS renegotiation
- * -Dsun.security.ssl.allowLegacyHelloMessages=true          - to allow legacy TLS hello Messages
+   > run --lang turtle --keyStore src/test/resources/KEYSTORE.jks --ksPass secret --https 8443 test_www /2012/
 
-The sun.security options are described in more detail http://download.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#workarounds
-So to run the compiled jar you can use
-
-   > java -Djetty.ssl.keyStoreType=JKS -Djetty.ssl.keyStore=/Users/hjs/tmp/cert/KEYSTORE.jks -Djetty.ssl.keyStorePassword=secret -jar target/read-write-web.jar --https 8443 www_test /2011/09
-
-or to run sbt use the shorthand options in the bin/rwsbt.sh shell script eg
+   You can now request resources using curl over https using the command line
 
-  > bin/rwsbt.sh -n -sslUnsafe -sslLegacy
+   $ curl -k -i -H "Accept: application/rdf+xml" https://localhost:8443/2012/foaf.n3
 
-(exercise: improve the script so that all options can be set with it)
-### to enable debug add the following parameters after 'java'
+   In the test_www directory there is a meta.n3 file. If you move it to .meta.n3
 
-     -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
+   $ cd test_www
+   $ mv meta.n3 .meta.n3
 
+   Then the directory will be access controlled. You will need a functioning WebID to authenticate.
+
+### to enable debug mode
+
+    start sbt with the 
+
+    $ bin/rwsbt.sh -d      
+
+### to generate the eclipse configuration
+
+    > eclipse same-targets
+
+### to generate the IntelliJ configuration
+  
+    > gen-idea
+
+### to package the application as a stand-alone jar (creates target/read-write-web.jar)
+
+    > assembly
+
--- a/project/build.scala	Fri May 11 17:27:57 2012 +0200
+++ b/project/build.scala	Tue May 22 23:04:54 2012 +0200
@@ -29,7 +29,7 @@
   val htmlparser = "nu.validator.htmlparser" % "htmlparser" % "1.2.1"
   val grizzled = "org.clapper" %% "grizzled-scala" % "1.0.8" % "test"
   val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.4"
-  val argot =  "org.clapper" %% "argot" % "0.3.5"
+  val argot =  "org.clapper" %% "argot" % "0.4"
   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"
@@ -56,7 +56,7 @@
 object BuildSettings {
 
   val buildOrganization = "org.w3"
-  val buildVersion      = "0.1-SNAPSHOT"
+  val buildVersion      = "0.2-SNAPSHOT"
   val buildScalaVersion = "2.9.1"
 
   val buildSettings = Defaults.defaultSettings ++ Seq (
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/ReadWriteWebArgs.scala	Tue May 22 23:04:54 2012 +0200
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2011 World Wide Web Consortium
+ * under the W3C licence defined at http://opensource.org/licenses/W3C
+ */
+
+package org.w3.readwriteweb
+
+import java.io.File
+import java.net.{MalformedURLException, URL}
+import org.clapper.argot.{ArgotConversionException, ArgotParser, ArgotConverters}
+import org.slf4j.{LoggerFactory, Logger}
+import org.w3.readwriteweb.auth.X509CertSigner
+
+
+/**
+ * @author bblfish
+ * @created 22/05/2012
+ */
+
+trait ReadWriteWebArgs {
+  val logger: Logger = LoggerFactory.getLogger(this.getClass)
+
+  // in Order to be receptive to DNS changes the DNS cache properties below must be set
+  // please tune them to see what works best
+  java.security.Security.setProperty("networkaddress.cache.ttl" , ""+60*10);
+  java.security.Security.setProperty("networkaddress.cache.negative.ttl",""+60*3) //3 minutes
+  import ArgotConverters._
+
+  val postUsageMsg= Some(
+    """
+      |NOTES
+      |
+      |  - One of --http or --https must be selected
+      |  - Trust stores are not needed because we use the WebID protocol, and client certs are nearly never signed by CAs
+      |
+      |SYSTEM PROPERTIES
+      |
+      |  You can also set the following system properties with -Dproperty=value
+      |
+      |  SECURITY:
+      |  * ssl.keyStore: location of keystore (also --ks )
+      |  * ssl.keyStorePassword: keystore password (so you don't need to type it on command line)
+      |  * ssl.keyStoreType: keystore type (eg. JKS) where the server sertificate is located
+      |
+      |EXAMPLES
+      |
+      | from sbt shell in the source directory
+      | > run --lang turtle --keyStore src/test/resources/KEYSTORE.jks --ksPass secret --https 8443 test_www /2012/
+      |
+    """.stripMargin);
+
+  val parser = new ArgotParser("read-write-web",
+    outputWidth = 120,
+    preUsage = Some("Version 0.2"), postUsage = postUsageMsg,
+    sortUsage = false
+  )
+
+  val mode = parser.flag[RWWMode](List("wiki"), List("strict"),
+    "all resources already exist in --wiki mode (you can GET them) but not in --strict mode") {
+    (sValue, opt) =>
+      sValue match {
+        case true => AllResourcesAlreadyExist
+        case false => ResourcesDontExistByDefault
+        case _ => throw new ArgotConversionException("Option %s: must be either wiki or strict" format (opt.name, sValue))
+      }
+  }
+
+  val host = parser.option[String](List("host"),"domain","the domain of your machine (localhost otherwise) - not sure where this ends up revealing itself")
+
+  val rdfLanguage = parser.option[Lang](List("language","lang"), "l", "RDF language files are stored as: turtle or rdfxml") {
+    (sValue, opt) =>
+      sValue match {
+        case "n3" => N3
+        case "turtle" => TURTLE
+        case "rdfxml" => RDFXML
+        case _ => throw new ArgotConversionException("Option %s: must be either n3, turtle or rdfxml" format (opt.name, sValue))
+      }
+  }
+
+
+  /**
+   * set environmental variables, but adapt them for the underlying server
+   * (a silly hack because jetty and netty in unfiltered use different TLS security
+   * properties, and I don't want to dig through each library to set that right just now)
+   * @param name
+   * @param value
+   */
+  def setPropHack(name: String, value: String)
+
+  private val keyStoreOpt = parser.option[String](List("keyStore","ks"),"url",
+    "path to keystore (same as system property ssl.keyStore )")
+
+  //this should only be called after parsing
+  //todo: The ArgotParsers options cannot deal with default values, which is why we have to do this ugly hack
+  //it would require some mothod like orElse
+  lazy val keyStore =  keyStoreOpt.value.orElse{
+    Option(System.getProperty("ssl.keyStore"))
+  }.map(p => new URL(new File(".").toURI.toURL,p))
+
+
+  private val keyStoreTypeOpt = parser.option[String](List("ksType"),"t","Type of KeyStore (JKS by default)") {
+    (sValue, opt) =>
+      sValue match {
+        case tp: String => tp
+        case _ => "JKS"
+      }
+  }
+
+  //this should only be called after parsing
+  lazy val keyStoreType =  keyStoreTypeOpt.value.orElse{
+    Option(System.getProperty("ssl.keyStoreType"))
+  }
+
+  val keyStoreAlias = parser.option[String](List("ksAlias"),"name","alias for the key in the keystore")
+
+  private val keyStorePasswordOpt = parser.option[String](List("ksPass"),"pass",
+    "password for keystore (better set ssl.keyStorePassword System property)")
+
+  lazy val keyStorePassword = keyStorePasswordOpt.value.orElse{
+    Option(System.getProperty("ssl.keyStorePassword"))
+  }
+
+  val clientTLSsecurity = parser.option[Boolean](List("clientTLS"),"mode",
+    """client TLS connection security mode:
+      | * secure: if server certificate is not signed by well known CA don't accept
+      | * noCA: if the server certificate is not signed by well known CA ignore and continue
+      | * noDomain: for test situations where the server cert does not name its server correctly
+      | * [todo: add more flexible server certificate verification mechanisms]
+    """.stripMargin) {
+    (sValue, opt) =>
+      sValue match {
+        case "noCA" => {
+          //todo: work with system property as a hack for the moment, as passing around conexts is going to require
+          //      a lot of rewriting
+          System.setProperty("rww.clientTLSsecurity","noCA")
+          false
+        }
+        case "noDomain" => {
+          System.setProperty("rww.clientTLSsecurity","noDomain")
+          false
+        }
+        case _ => {
+          System.setProperty("rww.clientTLSsecurity","secure")
+          true
+        }
+      }
+  }
+
+  parser.option[Unit](List("sslReneg"),"type",
+    """enable workaround for SSL regegotiation issue RFC5746 (see http://tinyurl.com/d9ydrnw )
+      | * sslUnsafe: allow unsafe renegotiation (will work with older browsers)
+      | * sslLegacy: allow legacy handshake without RFC 5746 messages
+      | (otherwise will use the default of the JVM used)
+    """.stripMargin) {
+    (sValue, opt) =>
+      sValue match {
+        case "sslUnsafe" => System.setProperty("sun.security.ssl.allowUnsafeRenegotiation","true")
+        case "sslLegacy" => System.setProperty("sun.security.ssl.allowLegacyHelloMessages","true")
+        case _ => ()
+      }
+  }
+
+  parser.flag[Unit](List("sslDebug"),"trace all SSL messages to output") {
+    (select, opt) => if (select) System.setProperty("javax.net.debug","all")
+  }
+
+  val proxy = parser.option[URL](List("proxy"),"p",
+    "Proxy to use when making client http(s) connections (sets http.proxy system property)") {
+    (sValue, opt) =>
+      try {
+        val proxy = new URL(sValue);
+        System.getProperties().put("http.proxy", proxy);
+        proxy
+      } catch {
+        case e: MalformedURLException => throw new ArgotConversionException("Option %s: url does not parse correctly "
+          format (opt.name, sValue))
+      }
+  }
+
+  val httpPort = parser.option[Int]("http", "port","start the http server on port")
+  val httpsPort = parser.option[Int]("https","port","start the https server on port")
+
+  val rootDirectory = parser.parameter[File]("rootDirectory", "path to root directory where files are served", false) {
+    (sValue, opt) => {
+      val file = new File(sValue)
+      if (! file.exists)
+        throw new ArgotConversionException("Option %s: %s must be a valid path" format (opt.name, sValue))
+      else
+        file
+    }
+  }
+
+  lazy val signer =
+    X509CertSigner( keyStore, keyStoreType,
+      keyStorePassword,  keyStoreAlias.value.orElse(Some("selfsigned")) )
+
+  val baseURL = parser.parameter[String]("baseURL", "the base path in the URL from which resources will be served", false)
+
+  /** execute after parseing command line */
+  protected def postParse {
+    keyStore.map(u=>setPropHack("ssl.keyStore",u.getPath))
+    keyStorePassword.map(setPropHack("ssl.keyStorePassword",_))
+    keyStoreType.map(setPropHack("ssl.keyStoreType",_))
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/ReadWriteWebJetty.scala	Tue May 22 23:04:54 2012 +0200
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 World Wide Web Consortium
+ * under the W3C licence defined at http://opensource.org/licenses/W3C
+ */
+
+
+package org.w3.readwriteweb
+
+import auth.{X509CertSigner, RDFAuthZ, X509view}
+import org.w3.readwriteweb.util._
+
+import unfiltered.jetty._
+import Console.err
+import org.slf4j.{Logger, LoggerFactory}
+
+import org.clapper.argot._
+import ArgotConverters._
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+import java.lang.String
+import java.io.File
+import java.net.{MalformedURLException, URL}
+
+
+object ReadWriteWebJetty extends ReadWriteWebArgs {
+
+  import unfiltered.filter.Planify
+
+  // regular Java main
+  def main(args: Array[String]) {
+
+    try {
+      parser.parse(args)
+      postParse
+    } catch {
+      case e: ArgotUsageException => err.println(e.message); sys.exit(1)
+    }
+
+
+    val filesystem =
+      new Filesystem(
+        rootDirectory.value.get,
+        baseURL.value.get,
+        lang=rdfLanguage.value getOrElse RDFXML)(mode.value getOrElse ResourcesDontExistByDefault)
+    
+    val rww = new ReadWriteWeb[HttpServletRequest,HttpServletResponse] {
+      val rm = filesystem
+      def manif = manifest[HttpServletRequest]
+      override implicit val authz = new RDFAuthZ[HttpServletRequest,HttpServletResponse](filesystem)
+    }
+
+
+    //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 HttpsTrustAll(port,host.value.getOrElse("0.0.0.0"))
+      case None => Http(httpPort.value.get,host.value.getOrElse("0.0.0.0"))
+    }
+
+    // configures and launches a Jetty server
+    service.filter(new FilterLogger(logger)).
+      context("/public"){ ctx:ContextBuilder =>
+        ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
+    }.filter(Planify(JettyEchoPlan.intent)).
+      filter(Planify(rww.intent)).
+      filter(Planify(x509v.intent)).
+      run()
+    
+  }
+
+
+
+  object x509v extends X509view[HttpServletRequest,HttpServletResponse] {
+    def manif = manifest[HttpServletRequest]
+  }
+
+  /**
+   * set environmental variables, but adapt them for the underlying server
+   * (a silly hack because jetty and netty in unfiltered use different TLS security
+   * properties, and I don't want to dig through each library to set that right just now)
+   * @param name
+   * @param value
+   */
+  def setPropHack(name: String, value: String) {
+    System.setProperty("jetty."+name,value)
+  }
+}
+
+
--- a/src/main/scala/ReadWriteWebMain.scala	Fri May 11 17:27:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-package org.w3.readwriteweb
-
-import auth.{X509CertSigner, RDFAuthZ, X509view}
-import org.w3.readwriteweb.util._
-
-import unfiltered.jetty._
-import Console.err
-import org.slf4j.{Logger, LoggerFactory}
-
-import org.clapper.argot._
-import ArgotConverters._
-import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
-import java.lang.String
-import java.io.File
-import java.net.{MalformedURLException, URL}
-
-trait ReadWriteWebArgs {
-  val logger: Logger = LoggerFactory.getLogger(this.getClass)
-
-  // in Order to be receptive to DNS changes the DNS cache properties below must be set
-  // please tune them to see what works best
-  java.security.Security.setProperty("networkaddress.cache.ttl" , ""+60*10);
-  java.security.Security.setProperty("networkaddress.cache.negative.ttl",""+60*3) //3 minutes
-
-
-  val postUsageMsg= Some("""
-  |PROPERTIES
-  |
-  | * Keystore properties that need to be set if https is started
-  |  -Dnetty.ssl.keyStoreType=type : the type of the keystore, JKS by default usually
-  |  -Dnetty.ssl.keyStore=path : specify path to key store (for https server certificate)
-  |  -Dnetty.ssl.keyStorePassword=password : specify password for keystore store (optional)
-  |  (for jetty, replace "netty" with "jetty")
-  |
-  | * application arguments:
-  |  --http  start server as plain http server
-  |  --https start server as in secured mode using https (TLS)
-  |  --language [turtle, rdfxml] save RDF in one of the given formats on disk
-  |  --clientTLS [secure, noCA, noDomain] client connections abide by CA verification
-  |   * secure : if server certificate is not signed by well known CA don't accept
-  |   * noCA: if the server certificate is not signed by well known CA ignore and continue
-  |   * noDomain: for test situations where the server certificate does not even name the machine it is on correctly
-  |   * [todo: add more flexible server certificate verification mechanisms]
-  |  --proxy url: url in the form http://host.example:port
-  |
-  |NOTES
-  |
-  |  - Trust stores are not needed because we use the WebID protocol, and client certs are nearly never signed by CAs
-  |  - one of --http or --https must be selected
-  |
-     """.stripMargin);
-
-  val parser = new ArgotParser("read-write-web",postUsage=postUsageMsg)
-
-  val mode = parser.option[RWWMode](List("mode"), "m", "wiki mode: wiki or strict") {
-    (sValue, opt) =>
-      sValue match {
-        case "wiki" => AllResourcesAlreadyExist
-        case "strict" => ResourcesDontExistByDefault
-        case _ => throw new ArgotConversionException("Option %s: must be either wiki or strict" format (opt.name, sValue))
-      }
-    }
-
-  val rdfLanguage = parser.option[Lang](List("language"), "l", "RDF language") {
-    (sValue, opt) =>
-      sValue match {
-        case "n3" => N3
-        case "turtle" => TURTLE
-        case "rdfxml" => RDFXML
-        case _ => throw new ArgotConversionException("Option %s: must be either n3, turtle or rdfxml" format (opt.name, sValue))
-      }
-  }
-
-  val clientTLSsecurity = parser.option[Boolean](List("clientTLS"),"c","client TLS connection security level") {
-    (sValue, opt) =>
-      sValue match {
-        case "noCA" => {
-          //todo: work with system property as a hack for the moment, as passing around conexts is going to require
-          //      a lot of rewriting
-          System.setProperty("rww.clientTLSsecurity","noCA")
-          false
-        }
-        case "noDomain" => {
-          System.setProperty("rww.clientTLSsecurity","noDomain")
-          false
-        }
-        case _ => {
-          System.setProperty("rww.clientTLSsecurity","secure")
-          true
-        }
-      }
-  }
-
-  val proxy = parser.option[URL](List("proxy"),"p","Proxy to use when making client http(s) connections") {
-    (sValue, opt) =>
-        try {
-          val proxy = new URL(sValue);
-          System.getProperties().put("http.proxy", proxy);
-          proxy
-        } catch {
-          case e: MalformedURLException => throw new ArgotConversionException("Option %s: url does not parse correctly "
-            format (opt.name, sValue))
-        }
-  }
-
-  val httpPort = parser.option[Int]("http", "Port","start the http server on port")
-  val httpsPort = parser.option[Int]("https","port","start the https server on port")
-
-  val rootDirectory = parser.parameter[File]("rootDirectory", "root directory", false) {
-    (sValue, opt) => {
-      val file = new File(sValue)
-      if (! file.exists)
-        throw new ArgotConversionException("Option %s: %s must be a valid path" format (opt.name, sValue))
-      else
-        file
-    }
-  }
-
-  val signer = {
-    val keystore = new File(System.getProperty( "netty.ssl.keyStore")).toURI.toURL
-    val ksTpe = System.getProperty("netty.ssl.keyStoreType","JKS")
-    val ksPass = System.getProperty("netty.ssl.keyStorePassword")
-    val alias = System.getProperty("netty.ssl.keyAlias","selfsigned")
-    X509CertSigner( keystore, ksTpe, ksPass,  alias )
-  }
-
-  val baseURL = parser.parameter[String]("baseURL", "base URL", false)
-
-}
-
-
-
-object ReadWriteWebMain extends ReadWriteWebArgs {
-
-  import unfiltered.filter.Planify
-
-  // regular Java main
-  def main(args: Array[String]) {
-
-    try {
-      parser.parse(args)
-    } catch {
-      case e: ArgotUsageException => err.println(e.message); sys.exit(1)
-    }
-
-
-    val filesystem =
-      new Filesystem(
-        rootDirectory.value.get,
-        baseURL.value.get,
-        lang=rdfLanguage.value getOrElse RDFXML)(mode.value getOrElse ResourcesDontExistByDefault)
-    
-    val rww = new ReadWriteWeb[HttpServletRequest,HttpServletResponse] {
-      val rm = filesystem
-      def manif = manifest[HttpServletRequest]
-      override implicit val authz = new RDFAuthZ[HttpServletRequest,HttpServletResponse](filesystem)
-    }
-
-
-    //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 HttpsTrustAll(port,"0.0.0.0")
-      case None => Http(httpPort.value.get)
-    }
-
-    // configures and launches a Jetty server
-    service.filter(new FilterLogger(logger)).
-      context("/public"){ ctx:ContextBuilder =>
-        ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
-    }.filter(Planify(JettyEchoPlan.intent)).
-      filter(Planify(rww.intent)).
-      filter(Planify(x509v.intent)).
-      run()
-    
-  }
-
-
-
-  object x509v extends X509view[HttpServletRequest,HttpServletResponse] {
-    def manif = manifest[HttpServletRequest]
-  }
-
-}
-
-
--- a/src/main/scala/auth/WebIDSrvc.scala	Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/auth/WebIDSrvc.scala	Tue May 22 23:04:54 2012 +0200
@@ -55,7 +55,7 @@
  */
 trait WebIDSrvc[Req,Res] {
   implicit def manif: Manifest[Req]
-  val signer: X509CertSigner
+  val signer: Option[X509CertSigner]
 
   import WebIDSrvc._
 
@@ -63,8 +63,10 @@
 
   def sign(urlStr: String): URL = {
     val timeStampedUrlStr = urlStr + "ts=" + URLEncoder.encode(dateFormat.format(Calendar.getInstance.getTime), "UTF-8")
-    val signedUri = timeStampedUrlStr +
-      "&sig=" + URLEncoder.encode(new String(Base64.encodeBase64URLSafeString(signer.sign(timeStampedUrlStr))), "UTF-8")
+    val signedUri = signer.map(sgnr =>
+      timeStampedUrlStr +
+      "&sig=" + URLEncoder.encode(new String(Base64.encodeBase64URLSafeString(sgnr.sign(timeStampedUrlStr))), "UTF-8")
+    ).getOrElse(timeStampedUrlStr)
     return new URL(signedUri)
   }
 
@@ -148,9 +150,9 @@
   }
   
   object aboutTransform extends Transformer {
-    val key = signer.signingCert.getPublicKey.asInstanceOf[RSAPublicKey]
-    $(".modulus").contents = key.getModulus.toString(16)
-    $(".exponent").contents = key.getPublicExponent.toString
+    val key = signer.map(_.signingCert.getPublicKey.asInstanceOf[RSAPublicKey])
+    $(".modulus").contents = key.map(_.getModulus.toString(16)).getOrElse("no key, no modulus - no signature - contact admin!")
+    $(".exponent").contents = key.map(_.getPublicExponent.toString).getOrElse("no key, no exponent")
   }
 
   class ServiceTrans(relyingParty: URL, claim: XClaim) extends Transformer {
--- a/src/main/scala/auth/X509Cert.scala	Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/auth/X509Cert.scala	Tue May 22 23:04:54 2012 +0200
@@ -37,25 +37,47 @@
 import org.w3.readwriteweb.util.trySome
 import actors.threadpool.TimeUnit
 import com.google.common.cache.{CacheLoader, CacheBuilder, Cache}
-
-object X509CertSigner {
+import scalaz.Validation
+import scalaz.Scalaz._
 
-  def apply(
-      keyStoreLoc: URL,
-      keyStoreType: String,
-      password: String,
-      alias: String): X509CertSigner = {
-    val keystore = KeyStore.getInstance(keyStoreType)
+import com.weiglewilczek.slf4s.Logging
 
-    IO.use(keyStoreLoc.openStream()) { in =>
-      keystore.load(in, password.toCharArray)
+object X509CertSigner extends Logging {
+
+  def apply( keyStoreLoc: Option[URL],
+             keyStoreType: Option[String],
+             password: Option[String],
+             alias: Option[String]): Option[X509CertSigner] = {
+    try {
+      for {
+        loc <- keyStoreLoc
+        tp <- keyStoreType
+      } yield {
+        val pass = password.map(_.toCharArray).getOrElse(null)
+        val alias2 = alias.getOrElse("")  //todo there are better ways of finding an alias than this
+        val ks = KeyStore.getInstance(tp)
+        IO.use(loc.openStream()) {
+          in => ks.load(in, pass)
+        }
+        val privateKey = ks.getKey(alias2, pass).asInstanceOf[PrivateKey]
+        val certificate = ks.getCertificate(alias2).asInstanceOf[X509Certificate]
+        //one could verify that indeed this is the private key corresponding to the public key in the cert.
+        new X509CertSigner(certificate, privateKey)
+      }
+    } catch {
+      case e: Exception => {
+        logger.warn("could not load TLS certificate for certificate signing service", e)
+        None
+      }
     }
-    val privateKey = keystore.getKey(alias, password.toCharArray).asInstanceOf[PrivateKey]
-    val certificate = keystore.getCertificate(alias).asInstanceOf[X509Certificate]
-    //one could verify that indeed this is the private key corresponding to the public key in the cert.
+  }
 
-    new X509CertSigner(certificate, privateKey)
-  }
+  def apply( keyStoreLoc: URL,
+             keyStoreType: String,
+             password: String,
+             alias: String): X509CertSigner =
+    apply(Option(keyStoreLoc),Option(keyStoreType),Option(password),Option(alias)).get
+
 }
 
 class X509CertSigner(
--- a/src/main/scala/netty/ReadWriteWebNetty.scala	Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/netty/ReadWriteWebNetty.scala	Tue May 22 23:04:54 2012 +0200
@@ -35,6 +35,7 @@
 import unfiltered.response._
 import unfiltered.netty._
 import collection.immutable.List
+import scala.None
 
 /**
  * ReadWrite Web for Netty server, allowing TLS renegotiation
@@ -50,6 +51,7 @@
 
      try {
        parser.parse(args)
+       postParse
      } catch {
        case e: ArgotUsageException => err.println(e.message); sys.exit(1)
      }
@@ -68,19 +70,24 @@
 
      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)
-       case None => new KeyAuth_Https(httpPort.value.get)
+     val domain = host.value.getOrElse("0.0.0.0")
+     //only one of the following two servers can be started at one time
+     //having two would require a lot more care with issues of overwriting
+     httpsPort.value.map {
+       port => new KeyAuth_Https(port).
+         plan(publicStatic).
+         plan( echo ).
+         plan( x509v ).
+         plan( webidp ).
+         plan( rww ).run()
      }
 
-
-     // configures and launches a Netty server
-     service.plan(publicStatic).
-       plan( echo ).
-       plan( x509v ).
-       plan( webidp ).
-       plan( rww ).run()
+     httpPort.value.map {
+      port => Http(httpPort.value.get,domain).
+        plan(publicStatic).
+        plan( echo ).
+        plan( rww ).run()
+    }
      
    }
 
@@ -137,6 +144,16 @@
     val signer = ReadWriteWebNetty.signer
   }
 
+  /**
+   * set environmental variables, but adapt them for the underlying server
+   * (a silly hack because jetty and netty in unfiltered use different TLS security
+   * properties, and I don't want to dig through each library to set that right just now)
+   * @param name
+   * @param value
+   */
+  def setPropHack(name: String, value: String) {
+    System.setProperty("netty."+name,value)
+  }
 }
 
 
--- a/src/main/scala/netty/SslLoginTest.scala	Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/netty/SslLoginTest.scala	Tue May 22 23:04:54 2012 +0200
@@ -93,7 +93,7 @@
   }
 
 
-  def main(args: Array[String]) {
-    new KeyAuth_Https(8443).plan(SslLoginTest).run()
-  }
+//  def main(args: Array[String]) {
+//    new KeyAuth_Https(8443).plan(SslLoginTest).run()
+//  }
 }
\ No newline at end of file
--- a/src/main/scala/sommer/ResourceReader.scala	Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/sommer/ResourceReader.scala	Tue May 22 23:04:54 2012 +0200
@@ -179,6 +179,7 @@
        )
   } )
   
+/*
   def main(args: Array[String]) {
 
     val url: String = "http://bblfish.net/people/henry/card"
@@ -219,4 +220,5 @@
 //    }
 
   }
+*/
 }
\ No newline at end of file
--- a/test_www/.meta.n3	Fri May 11 17:27:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-@prefix acl: <http://www.w3.org/ns/auth/acl#> .
-@prefix foaf: <http://xmlns.com/foaf/0.1/> .
-@prefix : <#> .
-
-:a1 a acl:Authorization;
-   acl:accessTo <foaf.n3>;
-   acl:mode acl:Write;
-   acl:agent <http://bblfish.net/people/henry/card#me> .
-
-    
-:readAll a acl:Authorization;
-   acl:accessTo <foaf.n3>;
-   acl:mode acl:Read;
-   acl:agentClass foaf:Agent .
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test_www/meta.n3	Tue May 22 23:04:54 2012 +0200
@@ -0,0 +1,14 @@
+@prefix acl: <http://www.w3.org/ns/auth/acl#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix : <#> .
+
+:a1 a acl:Authorization;
+   acl:accessTo <foaf.n3>;
+   acl:mode acl:Write;
+   acl:agent <http://bblfish.net/people/henry/card#me> .
+
+    
+:readAll a acl:Authorization;
+   acl:accessTo <foaf.n3>;
+   acl:mode acl:Read;
+   acl:agentClass foaf:Agent .