Made the server context a more useful concept.
[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 = ThreadContext.current();
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 = ThreadContext.current();
35         return(perctx.put(ctx, val));
36     }
37     
38     public static Responder let(final Responder next, Object... params) {
39         final Map<ContextParam, Object> values = new HashMap<ContextParam, Object>();
40         if((params.length % 2) != 0)
41             throw(new IllegalArgumentException("SvcConfig.let takes only an even number of parameters"));
42         for(int i = 0; i < params.length; i += 2)
43             values.put((ContextParam)params[i], params[i + 1]);
44         
45         return(new Responder() {
46                 /* This can very well actually be set to something
47                  * of the wrong type, but since the result would,
48                  * obviously, be a ClassCastException either way,
49                  * this way is at least the more convenient. */
50                 @SuppressWarnings("unchecked")
51                 public void respond(Request req) {
52                     final Map<ContextParam, Object> old = new HashMap<ContextParam, Object>();
53                     Thread th = Thread.currentThread();
54                     for(Map.Entry<ContextParam, Object> val : values.entrySet()) {
55                         ContextParam p = val.getKey();
56                         synchronized(p) {
57                             if(p.perthr.containsKey(th))
58                                 old.put(p, p.perthr.get(th));
59                             p.perthr.put(th, val.getValue());
60                         }
61                     }
62                     try {
63                         next.respond(req);
64                     } finally {
65                         for(Map.Entry<ContextParam, Object> val : values.entrySet()) {
66                             ContextParam p = val.getKey();
67                             synchronized(p) {
68                                 if(old.containsKey(p)) {
69                                     p.perthr.put(th, old.get(p));
70                                 } else {
71                                     p.perthr.remove(th);
72                                 }
73                             }
74                         }
75                     }
76                 }
77             });
78     }
79 }