3 import dolda.jsvc.util.Misc;
4 import java.util.logging.*;
5 import java.lang.reflect.*;
8 public class ThreadContext extends ThreadGroup {
9 private Logger logger = Logger.getLogger("dolda.jsvc.context");
10 private ThreadGroup workers;
11 private long reqs = 0;
12 private final ServerContext ctx;
13 public final Responder root;
14 private int timelimit = 0;
15 private boolean forcelimit = false;
17 public ThreadContext(ThreadGroup parent, String name, ServerContext ctx, Class<?> bootclass) {
18 super((parent == null)?(Thread.currentThread().getThreadGroup()):parent, name);
20 workers = new ThreadGroup(this, "Worker threads") {
21 public void uncaughtException(Thread t, Throwable e) {
22 logger.log(Level.SEVERE, "Worker thread terminated with an uncaught exception", e);
27 tl = Integer.parseInt(ctx.sysconfig("jsvc.timelimit", "0"));
28 if((tl > 0) && ((timelimit == 0) || (tl < timelimit)))
30 tl = Integer.parseInt(ctx.libconfig("jsvc.timelimit", "0"));
31 if((tl > 0) && ((timelimit == 0) || (tl < timelimit)))
33 forcelimit |= Misc.boolval(ctx.sysconfig("jsvc.forcelimit", "0"));
34 forcelimit |= Misc.boolval(ctx.libconfig("jsvc.forcelimit", "0"));
36 root = bootstrap(bootclass);
39 (new WatchDog()).start();
42 private class WatchDog extends Thread {
43 private Map<RequestThread, State> state = new WeakHashMap<RequestThread, State>();
46 String st = "running";
51 super(ThreadContext.this, "Worker watchdog");
55 @SuppressWarnings("deprecation")
56 private long ckthread(long now, RequestThread rt) {
57 State st = state.get(rt);
62 if(st.st == "running") {
63 if(now - rt.stime() > timelimit) {
65 st.st = "interrupted";
69 return(timelimit - (now - rt.stime()));
71 } else if((st.st == "interrupted") || (st.st == "killed")) {
73 logger.log(Level.WARNING, "Thread " + rt + " refused to die; killing again");
74 if(now - st.lastkill > 5000) {
79 return(5000 - (now - st.lastkill));
88 long next = timelimit;
89 long now = System.currentTimeMillis();
90 Thread[] w = new Thread[workers.activeCount() + 5];
91 int num = workers.enumerate(w);
92 for(int i = 0; i < num; i++) {
93 if(w[i] instanceof RequestThread){
94 RequestThread rt = (RequestThread)w[i];
96 long n = ckthread(now, rt);
104 } catch(InterruptedException e) {
109 public void uncaughtException(Thread t, Throwable e) {
110 logger.log(Level.SEVERE, "Service thread " + t.toString() + " terminated with an uncaught exception", e);
113 public ServerContext server() {
117 public void shutdown() {
118 if(root instanceof ContextResponder)
119 ((ContextResponder)root).destroy();
123 long now = System.currentTimeMillis();
124 if(now - last > 10000) {
128 Thread[] th = new Thread[1];
129 if(enumerate(th) < 1)
133 } catch(InterruptedException e) {
134 logger.log(Level.WARNING, "Interrupted while trying to shut down all service threads. Some may remain.", e);
139 public RequestThread respond(Request req) {
140 return(new RequestThread(root, req, workers, "Worker thread " + reqs++));
143 private Responder bootstrap(final Class<?> bootclass) {
144 final Throwable[] err = new Throwable[1];
145 final Responder[] res = new Responder[1];
146 Thread boot = new Thread(this, "JSvc boot thread") {
149 Method cm = bootclass.getMethod("responder");
150 Object resp = cm.invoke(null);
151 if(!(resp instanceof Responder))
152 throw(new ClassCastException("JSvc bootstrapper did not return a responder"));
153 res[0] = (Responder)resp;
154 } catch(NoSuchMethodException e) {
155 logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
157 } catch(IllegalAccessException e) {
158 logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
160 } catch(InvocationTargetException e) {
161 logger.log(Level.SEVERE, "JSvc bootstrapper failed", e);
169 } catch(InterruptedException e) {
170 logger.log(Level.WARNING, "Interrupted during bootstrapping", e);
172 Thread.currentThread().interrupt();
176 throw(new RuntimeException(err[0]));
180 logger.log(Level.SEVERE, "No responder returned in spite of no error having happened.");
181 throw(new NullPointerException("No responder returned in spite of no error having happened."));
186 public static ThreadContext current() {
187 for(ThreadGroup tg = Thread.currentThread().getThreadGroup(); tg != null; tg = tg.getParent()) {
188 if(tg instanceof ThreadContext)
189 return((ThreadContext)tg);