A couple of bugfixes.
[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;
4b8346e1 10 private final ServerContext ctx;
c9837b5e
FT
11 public final Responder root;
12
4b8346e1 13 public ThreadContext(ThreadGroup parent, String name, ServerContext ctx, Class<?> bootclass) {
c9837b5e 14 super((parent == null)?(Thread.currentThread().getThreadGroup()):parent, name);
4b8346e1 15 this.ctx = ctx;
c9837b5e
FT
16 workers = new ThreadGroup(this, "Worker threads") {
17 public void uncaughtException(Thread t, Throwable e) {
18 logger.log(Level.SEVERE, "Worker thread terminated with an uncaught exception", e);
19 }
20 };
21 root = bootstrap(bootclass);
22 }
23
24 public void uncaughtException(Thread t, Throwable e) {
25 logger.log(Level.SEVERE, "Service thread " + t.toString() + " terminated with an uncaught exception", e);
26 }
27
4b8346e1
FT
28 public ServerContext server() {
29 return(ctx);
30 }
31
c9837b5e 32 public void shutdown() {
c9837b5e
FT
33 if(root instanceof ContextResponder)
34 ((ContextResponder)root).destroy();
a0b186f8
FT
35 try {
36 long last = 0;
37 while(true) {
38 long now = System.currentTimeMillis();
39 if(now - last > 10000) {
40 interrupt();
41 last = now;
42 }
43 Thread[] th = new Thread[1];
44 if(enumerate(th) < 1)
45 break;
46 th[0].join(10000);
47 }
48 } catch(InterruptedException e) {
49 logger.log(Level.WARNING, "Interrupted while trying to shut down all service threads. Some may remain.", e);
50 }
51 destroy();
c9837b5e
FT
52 }
53
54 public RequestThread respond(Request req) {
55 return(new RequestThread(root, req, workers, "Worker thread " + reqs++));
56 }
57
58 private Responder bootstrap(final Class<?> bootclass) {
59 final Throwable[] err = new Throwable[1];
60 final Responder[] res = new Responder[1];
61 Thread boot = new Thread(this, "JSvc boot thread") {
62 public void run() {
63 try {
64 Method cm = bootclass.getMethod("responder");
65 Object resp = cm.invoke(null);
66 if(!(resp instanceof Responder))
67 throw(new ClassCastException("JSvc bootstrapper did not return a responder"));
68 res[0] = (Responder)resp;
69 } catch(NoSuchMethodException e) {
70 logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
71 err[0] = e;
72 } catch(IllegalAccessException e) {
73 logger.log(Level.SEVERE, "Invalid JSvc bootstrapper specified", e);
74 err[0] = e;
75 } catch(InvocationTargetException e) {
76 logger.log(Level.SEVERE, "JSvc bootstrapper failed", e);
77 err[0] = e;
78 }
79 }
80 };
81 boot.start();
82 try {
83 boot.join();
84 } catch(InterruptedException e) {
85 logger.log(Level.WARNING, "Interrupted during bootstrapping", e);
86 boot.interrupt();
87 Thread.currentThread().interrupt();
88 }
83f55da4
FT
89 if(err[0] != null) {
90 destroy();
c9837b5e 91 throw(new RuntimeException(err[0]));
83f55da4 92 }
c9837b5e 93 if(res[0] == null) {
83f55da4 94 destroy();
c9837b5e
FT
95 logger.log(Level.SEVERE, "No responder returned in spite of no error having happened.");
96 throw(new NullPointerException("No responder returned in spite of no error having happened."));
97 }
98 return(res[0]);
99 }
4b8346e1
FT
100
101 public static ThreadContext current() {
102 for(ThreadGroup tg = Thread.currentThread().getThreadGroup(); tg != null; tg = tg.getParent()) {
103 if(tg instanceof ThreadContext)
104 return((ThreadContext)tg);
105 }
106 return(null);
107 }
c9837b5e 108}