Destroy the thread groups properly when shutting down.
[jsvc.git] / src / dolda / jsvc / ThreadContext.java
CommitLineData
c9837b5e
FT
1package dolda.jsvc;
2
3import java.util.logging.*;
4import java.lang.reflect.*;
5
6public 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() {
c9837b5e
FT
27 if(root instanceof ContextResponder)
28 ((ContextResponder)root).destroy();
a0b186f8
FT
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();
c9837b5e
FT
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}