Destroy the thread groups properly when shutting down.
[jsvc.git] / src / dolda / jsvc / ThreadContext.java
1 package dolda.jsvc;
2
3 import java.util.logging.*;
4 import java.lang.reflect.*;
5
6 public class ThreadContext extends ThreadGroup {
7     private Logger logger = Logger.getLogger("dolda.jsvc.context");
8     private ThreadGroup workers;
9     private long reqs = 0;
10     public final Responder root;
11     
12     public ThreadContext(ThreadGroup parent, String name, Class<?> bootclass) {
13         super((parent == null)?(Thread.currentThread().getThreadGroup()):parent, name);
14         workers = new ThreadGroup(this, "Worker threads") {
15                 public void uncaughtException(Thread t, Throwable e) {
16                     logger.log(Level.SEVERE, "Worker thread terminated with an uncaught exception", e);
17                 }
18             };
19         root = bootstrap(bootclass);
20     }
21     
22     public void uncaughtException(Thread t, Throwable e) {
23         logger.log(Level.SEVERE, "Service thread " + t.toString() + " terminated with an uncaught exception", e);
24     }
25     
26     public void shutdown() {
27         if(root instanceof ContextResponder)
28             ((ContextResponder)root).destroy();
29         try {
30             long last = 0;
31             while(true) {
32                 long now = System.currentTimeMillis();
33                 if(now - last > 10000) {
34                     interrupt();
35                     last = now;
36                 }
37                 Thread[] th = new Thread[1];
38                 if(enumerate(th) < 1)
39                     break;
40                 th[0].join(10000);
41             }
42         } catch(InterruptedException e) {
43             logger.log(Level.WARNING, "Interrupted while trying to shut down all service threads. Some may remain.", e);
44         }
45         destroy();
46     }
47     
48     public RequestThread respond(Request req) {
49         return(new RequestThread(root, req, workers, "Worker thread " + reqs++));
50     }
51     
52     private Responder bootstrap(final Class<?> bootclass) {
53         final Throwable[] err = new Throwable[1];
54         final Responder[] res = new Responder[1];
55         Thread boot = new Thread(this, "JSvc boot thread") {
56                 public void run() {
57                     try {
58                         Method cm = bootclass.getMethod("responder");
59                         Object resp = cm.invoke(null);
60                         if(!(resp instanceof Responder))
61                             throw(new ClassCastException("JSvc bootstrapper did not return a responder"));
62                         res[0] = (Responder)resp;
63                     } catch(NoSuchMethodException e) {
64                         logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
65                         err[0] = e;
66                     } catch(IllegalAccessException e) {
67                         logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
68                         err[0] = e;
69                     } catch(InvocationTargetException e) {
70                         logger.log(Level.SEVERE, "JSvc bootstrapper failed", e);
71                         err[0] = e;
72                     }
73                 }
74             };
75         boot.start();
76         try {
77             boot.join();
78         } catch(InterruptedException e) {
79             logger.log(Level.WARNING, "Interrupted during bootstrapping", e);
80             boot.interrupt();
81             Thread.currentThread().interrupt();
82         }
83         if(err[0] != null)
84             throw(new RuntimeException(err[0]));
85         if(res[0] == null) {
86             logger.log(Level.SEVERE, "No responder returned in spite of no error having happened.");
87             throw(new NullPointerException("No responder returned in spite of no error having happened."));
88         }
89         return(res[0]);
90     }
91 }