Merge branch 'master' of git.dolda2000.com:/srv/git/r/jsvc
authorFredrik Tolf <fredrik@dolda2000.com>
Mon, 19 Oct 2009 13:40:24 +0000 (15:40 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Mon, 19 Oct 2009 13:40:24 +0000 (15:40 +0200)
src/dolda/jsvc/RequestThread.java
src/dolda/jsvc/ServerContext.java
src/dolda/jsvc/ThreadContext.java
src/dolda/jsvc/j2ee/J2eeContext.java
src/dolda/jsvc/j2ee/Servlet.java
src/dolda/jsvc/j2ee/TomcatContext.java
src/dolda/jsvc/store/Store.java
src/dolda/jsvc/util/Misc.java

index 7940748..6ada741 100644 (file)
@@ -3,6 +3,7 @@ package dolda.jsvc;
 public class RequestThread extends Thread {
     private Request req;
     private Responder resp;
+    private long stime = 0;
     
     public RequestThread(Responder resp, Request req, ThreadGroup th, String name) {
        super(th, name);
@@ -11,6 +12,7 @@ public class RequestThread extends Thread {
     }
     
     public void run() {
+       stime = System.currentTimeMillis();
        resp.respond(req);
        try {
            req.output().close();
@@ -22,4 +24,8 @@ public class RequestThread extends Thread {
     public static Request request() {
        return(((RequestThread)Thread.currentThread()).req);
     }
+    
+    public long stime() {
+       return(stime);
+    }
 }
index acb27d6..423deba 100644 (file)
@@ -2,6 +2,7 @@ package dolda.jsvc;
 
 public interface ServerContext {
     public long starttime();
-    public String config(String key);
+    public String sysconfig(String key, String def);
+    public String libconfig(String key, String def);
     public String name();
 }
index 486bc91..27d90bf 100644 (file)
@@ -1,7 +1,9 @@
 package dolda.jsvc;
 
+import dolda.jsvc.util.Misc;
 import java.util.logging.*;
 import java.lang.reflect.*;
+import java.util.*;
 
 public class ThreadContext extends ThreadGroup {
     private Logger logger = Logger.getLogger("dolda.jsvc.context");
@@ -9,6 +11,8 @@ public class ThreadContext extends ThreadGroup {
     private long reqs = 0;
     private final ServerContext ctx;
     public final Responder root;
+    private int timelimit = 0;
+    private boolean forcelimit = false;
     
     public ThreadContext(ThreadGroup parent, String name, ServerContext ctx, Class<?> bootclass) {
        super((parent == null)?(Thread.currentThread().getThreadGroup()):parent, name);
@@ -18,7 +22,88 @@ public class ThreadContext extends ThreadGroup {
                    logger.log(Level.SEVERE, "Worker thread terminated with an uncaught exception", e);
                }
            };
+       
+       int tl;
+       tl = Integer.parseInt(ctx.sysconfig("jsvc.timelimit", "0"));
+       if((tl > 0) && ((timelimit == 0) || (tl < timelimit)))
+           timelimit = tl;
+       tl = Integer.parseInt(ctx.libconfig("jsvc.timelimit", "0"));
+       if((tl > 0) && ((timelimit == 0) || (tl < timelimit)))
+           timelimit = tl;
+       forcelimit |= Misc.boolval(ctx.sysconfig("jsvc.forcelimit", "0"));
+       forcelimit |= Misc.boolval(ctx.libconfig("jsvc.forcelimit", "0"));
+       
        root = bootstrap(bootclass);
+       
+       if(timelimit > 0)
+           (new WatchDog()).start();
+    }
+    
+    private class WatchDog extends Thread {
+       private Map<RequestThread, State> state = new WeakHashMap<RequestThread, State>();
+       
+       private class State {
+           String st = "running";
+           long lastkill;
+       }
+       
+       private WatchDog() {
+           super(ThreadContext.this, "Worker watchdog");
+           setDaemon(true);
+       }
+       
+       @SuppressWarnings("deprecation")
+       private long ckthread(long now, RequestThread rt) {
+           State st = state.get(rt);
+           if(st == null) {
+               st = new State();
+               state.put(rt, st);
+           }
+           if(st.st == "running") {
+               if(now - rt.stime() > timelimit) {
+                   rt.interrupt();
+                   st.st = "interrupted";
+                   st.lastkill = now;
+                   return(5000);
+               } else {
+                   return(timelimit - (now - rt.stime()));
+               }
+           } else if((st.st == "interrupted") || (st.st == "killed")) {
+               if(st.st == "killed")
+                   logger.log(Level.WARNING, "Thread " + rt + " refused to die; killing again");
+               if(now - st.lastkill > 5000) {
+                   rt.stop();
+                   st.st = "killed";
+                   st.lastkill = now;
+               } else {
+                   return(5000 - (now - st.lastkill));
+               }
+           }
+           return(timelimit);
+       }
+
+       public void run() {
+           try {
+               while(true) {
+                   long next = timelimit;
+                   long now = System.currentTimeMillis();
+                   Thread[] w = new Thread[workers.activeCount() + 5];
+                   int num = workers.enumerate(w);
+                   for(int i = 0; i < num; i++) {
+                       if(w[i] instanceof RequestThread){
+                           RequestThread rt = (RequestThread)w[i];
+                           if(rt.stime() > 0) {
+                               long n = ckthread(now, rt);
+                               if(n < next)
+                                   next = n;
+                           }
+                       }
+                   }
+                   Thread.sleep(next);
+               }
+           } catch(InterruptedException e) {
+           }
+       }
     }
     
     public void uncaughtException(Thread t, Throwable e) {
index bf1917c..84ce918 100644 (file)
@@ -9,12 +9,13 @@ import java.io.*;
 public abstract class J2eeContext implements ServerContext {
     private final ServletConfig sc;
     private final long ctime;
-    protected final Properties config;
+    protected final Properties sysconfig, libconfig;
     
     protected J2eeContext(ServletConfig sc) {
        this.sc = sc;
        this.ctime = System.currentTimeMillis();
-       config = new Properties();
+       sysconfig = new Properties();
+       libconfig = new Properties();
     }
     
     static J2eeContext create(ServletConfig sc) {
@@ -27,8 +28,16 @@ public abstract class J2eeContext implements ServerContext {
        return(ctime);
     }
     
-    public String config(String key) {
-       return((String)config.get(key));
+    public String sysconfig(String key, String def) {
+       return(sysconfig.getProperty(key, def));
+    }
+    
+    public String libconfig(String key, String def) {
+       return(libconfig.getProperty(key, def));
+    }
+    
+    void loadconfig(InputStream in) throws IOException {
+       libconfig.load(in);
     }
     
     public ServletConfig j2eeconfig() {
index d726474..082113c 100644 (file)
@@ -11,18 +11,18 @@ public class Servlet extends HttpServlet {
     private ThreadContext tg;
 
     public void init(ServletConfig cfg) throws ServletException {
-       Properties sprop = new Properties();
+       J2eeContext ctx = J2eeContext.create(cfg);
        try {
            InputStream pi = Servlet.class.getClassLoader().getResourceAsStream("jsvc.properties");
            try {
-               sprop.load(pi);
+               ctx.loadconfig(pi);
            } finally {
                pi.close();
            }
        } catch(IOException e) {
            throw(new Error(e));
        }
-       String clnm = (String)sprop.get("jsvc.bootstrap");
+       String clnm = ctx.libconfig("jsvc.bootstrap", null);
        if(clnm == null)
            throw(new ServletException("No JSvc bootstrapper specified"));
        Class<?> bc;
@@ -31,7 +31,6 @@ public class Servlet extends HttpServlet {
        } catch(ClassNotFoundException e) {
            throw(new ServletException("Invalid JSvc bootstrapper specified", e));
        }
-       ServerContext ctx = J2eeContext.create(cfg);
        String tgn;
        if(ctx.name() != null)
            tgn = "JSvc service for " + ctx.name();
index bf6312b..8672d39 100644 (file)
@@ -58,10 +58,10 @@ public class TomcatContext extends J2eeContext {
            logger.log(Level.WARNING, "no permissions to fetch Tomcat base directory while reading configuration", e);
            return;
        }
-       config.put("jsvc.storage", "file:" + new File(new File(base, "work"), "jsvc").getPath());
+       sysconfig.put("jsvc.storage", "file:" + new File(new File(base, "work"), "jsvc").getPath());
        File cdir = new File(base, "conf");
        try {
-           loadprops(config, new File(cdir, "jsvc.properties"));
+           loadprops(sysconfig, new File(cdir, "jsvc.properties"));
        } catch(SecurityException e) {
            logger.log(Level.WARNING, "no permssions to read from Tomcat conf directory while reading configuration", e);
        }
index 0664156..d32b3a2 100644 (file)
@@ -24,7 +24,7 @@ public abstract class Store implements Iterable<File> {
        ThreadContext ctx = ThreadContext.current();
        if(ctx == null)
            throw(new RuntimeException("Not running in jsvc context"));
-       String bn = ctx.server().config("jsvc.storage");
+       String bn = ctx.server().sysconfig("jsvc.storage", null);
        if(bn == null)
            throw(new RuntimeException("No storage root has been configured"));
        return(bn);
index 122a654..63a10a7 100644 (file)
@@ -122,4 +122,13 @@ public class Misc {
        }
        return(buf.toString());
     }
+    
+    public static boolean boolval(String val) {
+       val = val.trim().toLowerCase();
+       if(val.equals("1") || val.equals("on") || val.equals("true") || val.equals("yes") || val.equals("\u22a4"))
+           return(true);
+       if(val.equals("0") || val.equals("off") || val.equals("false") || val.equals("no") || val.equals("\u22a5"))
+           return(false);
+       throw(new IllegalArgumentException("value not recognized as boolean: " + val));
+    }
 }