Added a responder to serve static content from the classloader.
authorFredrik Tolf <fredrik@dolda2000.com>
Tue, 13 Oct 2009 01:21:07 +0000 (03:21 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Tue, 13 Oct 2009 01:21:07 +0000 (03:21 +0200)
13 files changed:
build.xml
src/dolda/jsvc/j2ee/Archive.java
src/dolda/jsvc/test/Bootstrap.java
src/dolda/jsvc/test/TestResponder.java
src/dolda/jsvc/test/static/foo/a [new file with mode: 0644]
src/dolda/jsvc/test/static/foo/b [new file with mode: 0644]
src/dolda/jsvc/test/static/index.html [new file with mode: 0644]
src/dolda/jsvc/test/static/test.css [new file with mode: 0644]
src/dolda/jsvc/util/Http.java [new file with mode: 0644]
src/dolda/jsvc/util/Misc.java
src/dolda/jsvc/util/Multiplexer.java
src/dolda/jsvc/util/Restarts.java
src/dolda/jsvc/util/StaticContent.java [new file with mode: 0644]

index c7a3065..4bd0d8d 100644 (file)
--- a/build.xml
+++ b/build.xml
     
     <target name="test-war" depends="build-env, jsvc-jar">
       <taskdef name="jsvc-war" classname="dolda.jsvc.j2ee.Archive$AntTask" classpath="build/jsvc.jar" />
+      <copy todir="build/test-bin/dolda/jsvc/test">
+       <fileset dir="src/dolda/jsvc/test">
+         <include name="static/**" />
+       </fileset>
+      </copy>
       <copy tofile="build/test-bin/jsvc.properties" file="etc/test.jsvc.properties" />
       <jar destfile="build/jsvc-test.jar" basedir="build/test-bin" />
       <jsvc-war destfile="build/jsvc-test.war">
index b5893be..4a4dc01 100644 (file)
@@ -1,5 +1,6 @@
 package dolda.jsvc.j2ee;
 
+import dolda.jsvc.util.*;
 import java.util.*;
 import java.io.*;
 import java.net.*;
@@ -64,16 +65,6 @@ public class Archive {
        return(props);
     }
 
-    private static void cpstream(InputStream in, OutputStream out) throws IOException {
-        byte[] buf = new byte[4096];
-        while(true) {
-           int ret = in.read(buf, 0, buf.length);
-            if(ret < 0)
-                return;
-           out.write(buf, 0, ret);
-        }
-    }
-
     private static class MissingPropException extends RuntimeException {
        public final String prop;
        
@@ -117,7 +108,7 @@ public class Archive {
     
     public void addcode(String name, InputStream in) throws IOException {
        zip().putNextEntry(new ZipEntry("WEB-INF/classes/" + name));
-       cpstream(in, zip());
+       Misc.cpstream(in, zip());
     }
 
     public void addjars(File[] jars) throws IOException {
@@ -127,7 +118,7 @@ public class Archive {
            zip.putNextEntry(new ZipEntry("WEB-INF/lib/" + jar.getName()));
            InputStream jarin = new FileInputStream(jar);
            try {
-               cpstream(jarin, zip);
+               Misc.cpstream(jarin, zip);
            } finally {
                jarin.close();
            }
index eb73996..ad4c38f 100644 (file)
@@ -5,6 +5,11 @@ import dolda.jsvc.util.*;
 
 public class Bootstrap {
     public static Responder responder() {
-       return(new ErrorHandler(new Rehandler(new TestResponder())));
+       Multiplexer root = new Multiplexer();
+       root.file("test", new TestResponder());
+       root.file("", new StaticContent(Bootstrap.class, "static/index.html", false, "text/html"));
+       root.file("css", new StaticContent(Bootstrap.class, "static/test.css", false, "text/css"));
+       root.dir("foo", new StaticContent(Bootstrap.class, "static/foo", true, "text/plain; charset=utf-8"));
+       return(Misc.stdroot(root));
     }
 }
index 3807a2a..e765d2e 100644 (file)
@@ -4,32 +4,16 @@ import dolda.jsvc.*;
 import dolda.jsvc.util.*;
 import java.io.*;
 
-public class TestResponder implements Responder {
-    public void respond(Request req) {
-       req.outheaders().put("Content-Type", "text/html; charset=utf-8");
-       PrintWriter out;
-       try {
-           out = new PrintWriter(new OutputStreamWriter(req.output(), "UTF-8"));
-       } catch(UnsupportedEncodingException e) {
-           throw(new Error(e));
-       }
-
-       if(req.path().equals("bard1"))
-           throw(new RuntimeException("bard1"));
-
-       out.println("<html>");
-       out.println("<head><title>Barda</title></head>");
-       out.println("<body>");
-       out.println("<h1>Barda</h1>");
-       out.println("Bardslen.");
+public class TestResponder extends SimpleWriter {
+    public TestResponder() {
+       super("plain");
+    }
+    
+    public void respond(Request req, PrintWriter out) {
+       out.println(req.url());
+       out.println(req.path());
        out.println(req.inheaders());
        out.println(req.ctx().starttime());
        out.println(req.remoteaddr() + "<->" + req.localaddr());
-       out.println("</body>");
-       out.println("</html>");
-
-       if(req.path().equals("bard2"))
-           throw(Restarts.redirectctx("/slen"));
-       out.flush();
     }
 }
diff --git a/src/dolda/jsvc/test/static/foo/a b/src/dolda/jsvc/test/static/foo/a
new file mode 100644 (file)
index 0000000..6e1f517
--- /dev/null
@@ -0,0 +1 @@
+Test A
diff --git a/src/dolda/jsvc/test/static/foo/b b/src/dolda/jsvc/test/static/foo/b
new file mode 100644 (file)
index 0000000..d4e395a
--- /dev/null
@@ -0,0 +1 @@
+Test B
diff --git a/src/dolda/jsvc/test/static/index.html b/src/dolda/jsvc/test/static/index.html
new file mode 100644 (file)
index 0000000..8553fc9
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
+<head>
+<title>Test</title>
+<link rel="stylesheet" title="Default style" type="text/css" href="css" />
+</head>
+<body>
+<h1>Hello world!</h1>
+<p>Test</p>
+</body>
+</html>
diff --git a/src/dolda/jsvc/test/static/test.css b/src/dolda/jsvc/test/static/test.css
new file mode 100644 (file)
index 0000000..eb67f25
--- /dev/null
@@ -0,0 +1,3 @@
+body {
+    font-family: sans;
+}
diff --git a/src/dolda/jsvc/util/Http.java b/src/dolda/jsvc/util/Http.java
new file mode 100644 (file)
index 0000000..000e224
--- /dev/null
@@ -0,0 +1,20 @@
+package dolda.jsvc.util;
+
+import java.util.*;
+import java.text.*;
+
+public class Http {
+    public final static DateFormat datefmt;
+    static {
+       datefmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
+       datefmt.setCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC")));
+    }
+    
+    public static String fmtdate(Date d) {
+       return(datefmt.format(d));
+    }
+    
+    public static Date parsedate(String str) throws ParseException {
+       return(datefmt.parse(str));
+    }
+}
index cc01f1f..e92b324 100644 (file)
@@ -1,6 +1,8 @@
 package dolda.jsvc.util;
 
+import dolda.jsvc.*;
 import java.util.*;
+import java.io.*;
 
 public class Misc {
     private static Map<Integer, String> stext = new HashMap<Integer, String>();
@@ -11,6 +13,7 @@ public class Misc {
        stext.put(301, "Permanently Moved");
        stext.put(302, "Temporarily Moved");
        stext.put(303, "See Other");
+       stext.put(304, "Not Modified");
        stext.put(400, "Bad Request");
        stext.put(401, "Authentication Required");
        stext.put(403, "Access Forbidden");
@@ -32,4 +35,21 @@ public class Misc {
            p = p.substring(1);
        return(p);
     }
+
+    public static void cpstream(InputStream in, OutputStream out) throws IOException {
+        byte[] buf = new byte[4096];
+        while(true) {
+           int ret = in.read(buf, 0, buf.length);
+            if(ret < 0)
+                return;
+           out.write(buf, 0, ret);
+        }
+    }
+    
+    public static Responder stdroot(Responder root) {
+       Responder ret = root;
+       ret = new Rehandler(ret);
+       ret = new ErrorHandler(ret);
+       return(ret);
+    }
 }
index dabcb34..df9e4c2 100644 (file)
@@ -16,18 +16,9 @@ public class Multiplexer implements Responder {
     }
     
     public Multiplexer() {
-       this(new SimpleWriter("html") {
-               public void respond(Request req, java.io.PrintWriter out) {
-                   req.status(404);
-                   out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-                   out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
-                   out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-US\">");
-                   out.println("<head><title>Resource not found</title></head>");
-                   out.println("<body>");
-                   out.println("<h1>Resource not found</h1>");
-                   out.println("The resource you requested could not be found on this server.");
-                   out.println("</body>");
-                   out.println("</html>");
+       this(new Responder() {
+               public void respond(Request req) {
+                   throw(Restarts.stdresponse(404, "Resource not found", "The resource you requested could not be found on this server."));
                }
            });
     }
index 1953667..7725f28 100644 (file)
@@ -2,6 +2,7 @@ package dolda.jsvc.util;
 
 import dolda.jsvc.*;
 import java.net.*;
+import java.io.*;
 
 public class Restarts {
     public static RequestRestart redirect(final URL to) {
@@ -43,4 +44,37 @@ public class Restarts {
                }
            });
     }
+    
+    public static RequestRestart stdresponse(final int code, final String title, final String message) {
+       return(new RequestRestart() {
+               public void respond(Request req) {
+                   req.status(code);
+                   req.outheaders().put("content-type", "text/html; charset=us-ascii");
+                   PrintWriter out;
+                   try {
+                       out = new PrintWriter(new OutputStreamWriter(req.output(), "US-ASCII"));
+                   } catch(UnsupportedEncodingException e) {
+                       throw(new Error(e));
+                   }
+                   out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+                   out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
+                   out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-US\">");
+                   out.println("<head><title>" + title + "</title></head>");
+                   out.println("<body>");
+                   out.println("<h1>" + title + "</h1>");
+                   out.println(message);
+                   out.println("</body>");
+                   out.println("</html>");
+                   out.flush();
+               }
+           });
+    }
+    
+    public static RequestRestart stdresponse(int code, String message) {
+       return(stdresponse(code, "An error occurred", message));
+    }
+
+    public static RequestRestart stdresponse(int code) {
+       return(stdresponse(code, "An error occurred", Misc.statustext(code)));
+    }
 }
diff --git a/src/dolda/jsvc/util/StaticContent.java b/src/dolda/jsvc/util/StaticContent.java
new file mode 100644 (file)
index 0000000..0d4208e
--- /dev/null
@@ -0,0 +1,66 @@
+package dolda.jsvc.util;
+
+import dolda.jsvc.*;
+import java.io.*;
+import java.util.*;
+
+public class StaticContent implements Responder {
+    private final Class<?> base;
+    private final String resname;
+    private final boolean dir;
+    private final String mimetype;
+    
+    public StaticContent(Class<?> base, String resname, boolean dir, String mimetype) {
+       this.base = base;
+       this.resname = resname;
+       this.dir = dir;
+       this.mimetype = mimetype;
+    }
+    
+    public StaticContent(String resname, boolean dir, String mimetype) {
+       this(null, resname, dir, mimetype);
+    }
+    
+    public void respond(Request req) {
+       String nm;
+       if(dir) {
+           nm = resname + "/" + req.path();
+       } else {
+           nm = resname;
+       }
+       InputStream in;
+       if(base == null) {
+           in = StaticContent.class.getClassLoader().getResourceAsStream(nm);
+       } else {
+           in = base.getResourceAsStream(nm);
+       }
+       if(in == null)
+           throw(Restarts.stdresponse(404));
+       String ims = req.inheaders().get("If-Modified-Since");
+       Date mtime = new Date((req.ctx().starttime() / 1000) * 1000);
+       if(ims != null) {
+           Date d;
+           try {
+               d = Http.parsedate(ims);
+           } catch(java.text.ParseException e) {
+               throw(Restarts.stdresponse(400));
+           }
+           if(mtime.compareTo(d) <= 0) {
+               req.status(304);
+               req.outheaders().put("Content-Length", "0");
+               return;
+           }
+       }
+       try {
+           try {
+               req.outheaders().put("Content-Type", mimetype);
+               req.outheaders().put("Last-Modified", Http.fmtdate(mtime));
+               Misc.cpstream(in, req.output());
+           } finally {
+               in.close();
+           }
+       } catch(IOException e) {
+           throw(new Error(e));
+       }
+    }
+}