1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.directwebremoting.guice;
17
18 import com.google.inject.Key;
19 import com.google.inject.Provider;
20 import com.google.inject.Scope;
21 import com.google.inject.util.ToStringBuilder;
22
23 import java.util.Collection;
24 import java.util.ArrayList;
25 import java.util.List;
26 import static java.util.Collections.synchronizedList;
27 import static java.util.Collections.unmodifiableList;
28
29 import java.util.concurrent.Callable;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentMap;
32 import java.util.concurrent.ExecutionException;
33 import java.util.concurrent.Future;
34 import java.util.concurrent.FutureTask;
35
36 import org.directwebremoting.util.Logger;
37 import static org.directwebremoting.guice.AbstractContextScope.State.*;
38
39 /**
40 * Partial implementation of {@link ContextScope}. Concrete implementations
41 * must pass the context identifier type to the super constructor and define
42 * {@code get()} to return the current context identifier (and to return null
43 * or throw an exception if there is no current context). They must also implement
44 * the {@link ContextRegistry} interface.
45 * @author Tim Peierls [tim at peierls dot net]
46 */
47 public abstract class AbstractContextScope<C, R>
48 implements ContextScope<C>, ContextRegistry<C, R>
49 {
50 protected AbstractContextScope(Class<C> type, String scopeName)
51 {
52 this.type = type;
53 this.scopeName = scopeName;
54 }
55
56 public String toString()
57 {
58 return scopeName;
59 }
60
61 public List<Key<?>> getKeysInScope()
62 {
63 synchronized (scopedKeys)
64 {
65 return new ArrayList<Key<?>>(scopedKeys);
66 }
67 }
68
69 public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator)
70 {
71 if (log.isDebugEnabled())
72 {
73 log.debug(String.format(
74 "scope %s: adding key %s with creator %s",
75 scopeName, key, creator
76 ));
77 }
78
79 scopedKeys.add(key);
80 final String name = key.toString();
81 return new Provider<T>()
82 {
83 public T get()
84 {
85 if (log.isDebugEnabled())
86 {
87 log.debug(String.format(
88 "scope %s: getting key %s with creator %s",
89 scopeName, key, creator
90 ));
91 }
92
93 C context = getContext(key);
94 R registry = registryFor(context);
95 InstanceProvider<T> future =
96 AbstractContextScope.this.get(registry, key, name);
97 if (future == null)
98 {
99 InstanceProvider<T> futureTask = new FutureTaskProvider<T>(creator);
100 future = putIfAbsent(registry, key, name, futureTask);
101 if (future == null)
102 {
103 future = futureTask;
104 futureTask.run();
105 if (Thread.currentThread().isInterrupted())
106 {
107 remove(registry, key, name, futureTask);
108 }
109 }
110 }
111 return future.get();
112 }
113
114 public String toString()
115 {
116 return new ToStringBuilder(this.getClass())
117 .add("scopeName", scopeName)
118 .add("type", type)
119 .add("key", key)
120 .add("creator", creator)
121 .toString();
122 }
123 };
124 }
125
126 public abstract C get();
127
128
129 public Class<C> type()
130 {
131 return type;
132 }
133
134 public Collection<C> getOpenContexts()
135 {
136 Collection<C> openContexts = new ArrayList<C>();
137 for (C context : contexts.keySet())
138 {
139 if (contexts.get(context) == OPEN)
140 {
141 openContexts.add(context);
142 }
143 }
144 return openContexts;
145 }
146
147 public void close(C context, ContextCloseHandler<?>... closeHandlers)
148 {
149 if (!contexts.replace(context, OPEN, CLOSED))
150 {
151
152 return;
153 }
154
155 for (InstanceProvider<?> provider : registeredProviders(registryFor(context)))
156 {
157 Object value = null;
158 try
159 {
160 value = provider.get();
161 }
162 catch (RuntimeException e)
163 {
164
165
166
167 }
168
169 if (value == null)
170 {
171
172 continue;
173 }
174
175 for (ContextCloseHandler<?> closeHandler : closeHandlers)
176 {
177 handleClose(closeHandler, value);
178 }
179 }
180 }
181
182 public void closeAll(ContextCloseHandler<?>... closeHandlers)
183 {
184 for (C context : getOpenContexts())
185 {
186 close(context, closeHandlers);
187 }
188 }
189
190 private <T> void handleClose(ContextCloseHandler<T> closeHandler, Object value)
191 {
192 Class<T> type = closeHandler.type();
193 if (type.isInstance(value))
194 {
195 try
196 {
197 closeHandler.close(type.cast(value));
198 }
199 catch (Exception e)
200 {
201
202
203
204 }
205 }
206 }
207
208 private C getContext(Key<?> key)
209 {
210 C context = null;
211 RuntimeException caught = null;
212 try
213 {
214 context = get();
215 if (contexts.putIfAbsent(context, OPEN) == CLOSED)
216 {
217
218 context = null;
219 }
220 }
221 catch (RuntimeException ex)
222 {
223 caught = ex;
224 }
225 if (context == null)
226 {
227 throw new OutOfScopeException(this, key, caught);
228 }
229
230 return context;
231 }
232
233 private Collection<InstanceProvider<?>> registeredProviders(R registry)
234 {
235 List<InstanceProvider<?>> providers = new ArrayList<InstanceProvider<?>>();
236 for (Key<?> key : getKeysInScope())
237 {
238 InstanceProvider<?> provider = get(registry, key, key.toString());
239 if (provider != null)
240 {
241 providers.add(provider);
242 }
243 }
244 return providers;
245 }
246
247 enum State
248 {
249 OPEN,
250 CLOSED
251 }
252
253 private final Class<C> type;
254
255 private final String scopeName;
256
257
258 private final List<Key<?>> scopedKeys = synchronizedList(new ArrayList<Key<?>>());
259
260 private final ConcurrentMap<C, State> contexts = new ConcurrentHashMap<C, State>();
261
262 /**
263 * The log stream
264 */
265 private static final Logger log = Logger.getLogger(AbstractContextScope.class);
266 }