486bc91bb268fa5d7e1d5af06d569fd0c48dad00
[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     private final ServerContext ctx;
11     public final Responder root;
12     
13     public ThreadContext(ThreadGroup parent, String name, ServerContext ctx, Class<?> bootclass) {
14         super((parent == null)?(Thread.currentThread().getThreadGroup()):parent, name);
15         this.ctx = ctx;
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     
28     public ServerContext server() {
29         return(ctx);
30     }
31     
32     public void shutdown() {
33         if(root instanceof ContextResponder)
34             ((ContextResponder)root).destroy();
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();
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         }
89         if(err[0] != null) {
90             destroy();
91             throw(new RuntimeException(err[0]));
92         }
93         if(res[0] == null) {
94             destroy();
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     }
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     }
108 }