Improved on the SvcConfig and renamed it.
[jsvc.git] / src / dolda / jsvc / ContextParam.java
1 package dolda.jsvc;
2
3 import java.util.*;
4
5 public class ContextParam<T> {
6     private boolean bound;
7     private T value;
8     private final Object id = new Object();
9     private Map<ThreadContext, T> perctx = new WeakHashMap<ThreadContext, T>();
10     private Map<Thread, T> perthr = new WeakHashMap<Thread, T>();
11         
12     public ContextParam(T def) {
13         this.value = def;
14         this.bound = true;
15     }
16         
17     public ContextParam() {
18         this.bound = false;
19     }
20         
21     public synchronized T get() {
22         Thread th = Thread.currentThread();
23         if(perthr.containsKey(th))
24             return(perthr.get(th));
25         ThreadContext ctx = getctx();
26         if(perctx.containsKey(ctx))
27             return(perctx.get(ctx));
28         if(!bound)
29             throw(new IllegalStateException("No value is bound to this parameter."));
30         return(value);
31     }
32         
33     public synchronized T ctxset(T val) {
34         ThreadContext ctx = getctx();
35         return(perctx.put(ctx, val));
36     }
37     
38     private static ThreadContext getctx() {
39         for(ThreadGroup tg = Thread.currentThread().getThreadGroup(); tg != null; tg = tg.getParent()) {
40             if(tg instanceof ThreadContext)
41                 return((ThreadContext)tg);
42         }
43         return(null);
44     }
45     
46     public static Responder let(final Responder next, Object... params) {
47         final Map<ContextParam, Object> values = new HashMap<ContextParam, Object>();
48         if((params.length % 2) != 0)
49             throw(new IllegalArgumentException("SvcConfig.let takes only an even number of parameters"));
50         for(int i = 0; i < params.length; i += 2)
51             values.put((ContextParam)params[i], params[i + 1]);
52         
53         return(new Responder() {
54                 /* This can very well actually be set to something
55                  * of the wrong type, but since the result would,
56                  * obviously, be a ClassCastException either way,
57                  * this way is at least the more convenient. */
58                 @SuppressWarnings("unchecked")
59                 public void respond(Request req) {
60                     final Map<ContextParam, Object> old = new HashMap<ContextParam, Object>();
61                     Thread th = Thread.currentThread();
62                     for(Map.Entry<ContextParam, Object> val : values.entrySet()) {
63                         ContextParam p = val.getKey();
64                         synchronized(p) {
65                             if(p.perthr.containsKey(th))
66                                 old.put(p, p.perthr.get(th));
67                             p.perthr.put(th, val.getValue());
68                         }
69                     }
70                     try {
71                         next.respond(req);
72                     } finally {
73                         for(Map.Entry<ContextParam, Object> val : values.entrySet()) {
74                             ContextParam p = val.getKey();
75                             synchronized(p) {
76                                 if(old.containsKey(p)) {
77                                     p.perthr.put(th, old.get(p));
78                                 } else {
79                                     p.perthr.remove(th);
80                                 }
81                             }
82                         }
83                     }
84                 }
85             });
86     }
87 }