From: Fredrik Tolf Date: Mon, 19 Oct 2009 13:40:24 +0000 (+0200) Subject: Merge branch 'master' of git.dolda2000.com:/srv/git/r/jsvc X-Git-Url: http://www.dolda2000.com/gitweb/?a=commitdiff_plain;h=8becf9405020bb30b4b339ce954697faee99606b;hp=ed8f7296bfb39fbd66b79a113d4a72c6ade71fc0;p=jsvc.git Merge branch 'master' of git.dolda2000.com:/srv/git/r/jsvc --- diff --git a/src/dolda/jsvc/RequestThread.java b/src/dolda/jsvc/RequestThread.java index 7940748..6ada741 100644 --- a/src/dolda/jsvc/RequestThread.java +++ b/src/dolda/jsvc/RequestThread.java @@ -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); + } } diff --git a/src/dolda/jsvc/ServerContext.java b/src/dolda/jsvc/ServerContext.java index acb27d6..423deba 100644 --- a/src/dolda/jsvc/ServerContext.java +++ b/src/dolda/jsvc/ServerContext.java @@ -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(); } diff --git a/src/dolda/jsvc/ThreadContext.java b/src/dolda/jsvc/ThreadContext.java index 486bc91..27d90bf 100644 --- a/src/dolda/jsvc/ThreadContext.java +++ b/src/dolda/jsvc/ThreadContext.java @@ -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 state = new WeakHashMap(); + + 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) { diff --git a/src/dolda/jsvc/j2ee/J2eeContext.java b/src/dolda/jsvc/j2ee/J2eeContext.java index bf1917c..84ce918 100644 --- a/src/dolda/jsvc/j2ee/J2eeContext.java +++ b/src/dolda/jsvc/j2ee/J2eeContext.java @@ -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() { diff --git a/src/dolda/jsvc/j2ee/Servlet.java b/src/dolda/jsvc/j2ee/Servlet.java index d726474..082113c 100644 --- a/src/dolda/jsvc/j2ee/Servlet.java +++ b/src/dolda/jsvc/j2ee/Servlet.java @@ -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(); diff --git a/src/dolda/jsvc/j2ee/TomcatContext.java b/src/dolda/jsvc/j2ee/TomcatContext.java index bf6312b..8672d39 100644 --- a/src/dolda/jsvc/j2ee/TomcatContext.java +++ b/src/dolda/jsvc/j2ee/TomcatContext.java @@ -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); } diff --git a/src/dolda/jsvc/store/Store.java b/src/dolda/jsvc/store/Store.java index 0664156..d32b3a2 100644 --- a/src/dolda/jsvc/store/Store.java +++ b/src/dolda/jsvc/store/Store.java @@ -24,7 +24,7 @@ public abstract class Store implements Iterable { 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); diff --git a/src/dolda/jsvc/util/Misc.java b/src/dolda/jsvc/util/Misc.java index 122a654..63a10a7 100644 --- a/src/dolda/jsvc/util/Misc.java +++ b/src/dolda/jsvc/util/Misc.java @@ -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)); + } }