Made thread management more predictable.
[jsvc.git] / src / dolda / jsvc / ThreadContext.java
diff --git a/src/dolda/jsvc/ThreadContext.java b/src/dolda/jsvc/ThreadContext.java
new file mode 100644 (file)
index 0000000..f21d04c
--- /dev/null
@@ -0,0 +1,75 @@
+package dolda.jsvc;
+
+import java.util.logging.*;
+import java.lang.reflect.*;
+
+public class ThreadContext extends ThreadGroup {
+    private Logger logger = Logger.getLogger("dolda.jsvc.context");
+    private ThreadGroup workers;
+    private long reqs = 0;
+    public final Responder root;
+    
+    public ThreadContext(ThreadGroup parent, String name, Class<?> bootclass) {
+       super((parent == null)?(Thread.currentThread().getThreadGroup()):parent, name);
+       workers = new ThreadGroup(this, "Worker threads") {
+               public void uncaughtException(Thread t, Throwable e) {
+                   logger.log(Level.SEVERE, "Worker thread terminated with an uncaught exception", e);
+               }
+           };
+       root = bootstrap(bootclass);
+    }
+    
+    public void uncaughtException(Thread t, Throwable e) {
+       logger.log(Level.SEVERE, "Service thread " + t.toString() + " terminated with an uncaught exception", e);
+    }
+    
+    public void shutdown() {
+       interrupt();
+       if(root instanceof ContextResponder)
+           ((ContextResponder)root).destroy();
+    }
+    
+    public RequestThread respond(Request req) {
+       return(new RequestThread(root, req, workers, "Worker thread " + reqs++));
+    }
+    
+    private Responder bootstrap(final Class<?> bootclass) {
+       final Throwable[] err = new Throwable[1];
+       final Responder[] res = new Responder[1];
+       Thread boot = new Thread(this, "JSvc boot thread") {
+               public void run() {
+                   try {
+                       Method cm = bootclass.getMethod("responder");
+                       Object resp = cm.invoke(null);
+                       if(!(resp instanceof Responder))
+                           throw(new ClassCastException("JSvc bootstrapper did not return a responder"));
+                       res[0] = (Responder)resp;
+                   } catch(NoSuchMethodException e) {
+                       logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
+                       err[0] = e;
+                   } catch(IllegalAccessException e) {
+                       logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
+                       err[0] = e;
+                   } catch(InvocationTargetException e) {
+                       logger.log(Level.SEVERE, "JSvc bootstrapper failed", e);
+                       err[0] = e;
+                   }
+               }
+           };
+       boot.start();
+       try {
+           boot.join();
+       } catch(InterruptedException e) {
+           logger.log(Level.WARNING, "Interrupted during bootstrapping", e);
+           boot.interrupt();
+           Thread.currentThread().interrupt();
+       }
+       if(err[0] != null)
+           throw(new RuntimeException(err[0]));
+       if(res[0] == null) {
+           logger.log(Level.SEVERE, "No responder returned in spite of no error having happened.");
+           throw(new NullPointerException("No responder returned in spite of no error having happened."));
+       }
+       return(res[0]);
+    }
+}