OLD | NEW |
1 /** | 1 /** |
2 * Copyright (C) 2006 Google Inc. | 2 * Copyright (C) 2006 Google Inc. |
3 * | 3 * |
4 * Licensed under the Apache License, Version 2.0 (the "License"); | 4 * Licensed under the Apache License, Version 2.0 (the "License"); |
5 * you may not use this file except in compliance with the License. | 5 * you may not use this file except in compliance with the License. |
6 * You may obtain a copy of the License at | 6 * You may obtain a copy of the License at |
7 * | 7 * |
8 * http://www.apache.org/licenses/LICENSE-2.0 | 8 * http://www.apache.org/licenses/LICENSE-2.0 |
9 * | 9 * |
10 * Unless required by applicable law or agreed to in writing, software | 10 * Unless required by applicable law or agreed to in writing, software |
11 * distributed under the License is distributed on an "AS IS" BASIS, | 11 * distributed under the License is distributed on an "AS IS" BASIS, |
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 * See the License for the specific language governing permissions and | 13 * See the License for the specific language governing permissions and |
14 * limitations under the License. | 14 * limitations under the License. |
15 */ | 15 */ |
16 | 16 |
17 package com.google.inject.servlet; | 17 package com.google.inject.servlet; |
18 | 18 |
19 import com.google.common.base.Preconditions; | 19 import com.google.common.base.Preconditions; |
| 20 import com.google.common.collect.ImmutableSet; |
20 import com.google.common.collect.Maps; | 21 import com.google.common.collect.Maps; |
21 import com.google.inject.Key; | 22 import com.google.inject.Key; |
22 import com.google.inject.OutOfScopeException; | 23 import com.google.inject.OutOfScopeException; |
23 import com.google.inject.Provider; | 24 import com.google.inject.Provider; |
24 import com.google.inject.Scope; | 25 import com.google.inject.Scope; |
25 | 26 |
26 import java.util.Map; | 27 import java.util.Map; |
27 import java.util.concurrent.Callable; | 28 import java.util.concurrent.Callable; |
28 | 29 |
29 import javax.servlet.http.HttpServletRequest; | 30 import javax.servlet.http.HttpServletRequest; |
| 31 import javax.servlet.http.HttpServletResponse; |
30 import javax.servlet.http.HttpSession; | 32 import javax.servlet.http.HttpSession; |
31 | 33 |
32 /** | 34 /** |
33 * Servlet scopes. | 35 * Servlet scopes. |
34 * | 36 * |
35 * @author crazybob@google.com (Bob Lee) | 37 * @author crazybob@google.com (Bob Lee) |
36 */ | 38 */ |
37 public class ServletScopes { | 39 public class ServletScopes { |
38 | 40 |
39 private ServletScopes() {} | 41 private ServletScopes() {} |
40 | 42 |
| 43 /** Keys bound in request-scope which are handled directly by GuiceFilter. */ |
| 44 private static final ImmutableSet<Key<?>> REQUEST_CONTEXT_KEYS = ImmutableSet.
of( |
| 45 Key.get(HttpServletRequest.class), |
| 46 Key.get(HttpServletResponse.class), |
| 47 new Key<Map<String, String[]>>(RequestParameters.class) {}); |
| 48 |
41 /** A sentinel attribute value representing null. */ | 49 /** A sentinel attribute value representing null. */ |
42 enum NullObject { INSTANCE } | 50 enum NullObject { INSTANCE } |
43 | 51 |
44 /** | 52 /** |
45 * HTTP servlet request scope. | 53 * HTTP servlet request scope. |
46 */ | 54 */ |
47 public static final Scope REQUEST = new Scope() { | 55 public static final Scope REQUEST = new Scope() { |
48 public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { | 56 public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) { |
49 final String name = key.toString(); | 57 final String name = key.toString(); |
50 return new Provider<T>() { | 58 return new Provider<T>() { |
51 public T get() { | 59 public T get() { |
52 // Check if the alternate request scope should be used, if no HTTP | 60 // Check if the alternate request scope should be used, if no HTTP |
53 // request is in progress. | 61 // request is in progress. |
54 if (null == GuiceFilter.localContext.get()) { | 62 if (null == GuiceFilter.localContext.get()) { |
55 | 63 |
56 // NOTE(dhanji): We don't need to synchronize on the scope map | 64 // NOTE(dhanji): We don't need to synchronize on the scope map |
57 // unlike the HTTP request because we're the only ones who have | 65 // unlike the HTTP request because we're the only ones who have |
58 // a reference to it, and it is only available via a threadlocal. | 66 // a reference to it, and it is only available via a threadlocal. |
(...skipping 11 matching lines...) Expand all Loading... |
70 t = creator.get(); | 78 t = creator.get(); |
71 // Store a sentinel for provider-given null values. | 79 // Store a sentinel for provider-given null values. |
72 scopeMap.put(name, t != null ? t : NullObject.INSTANCE); | 80 scopeMap.put(name, t != null ? t : NullObject.INSTANCE); |
73 } | 81 } |
74 | 82 |
75 return t; | 83 return t; |
76 } // else: fall into normal HTTP request scope and out of scope | 84 } // else: fall into normal HTTP request scope and out of scope |
77 // exception is thrown. | 85 // exception is thrown. |
78 } | 86 } |
79 | 87 |
80 HttpServletRequest request = GuiceFilter.getRequest(); | 88 // Always synchronize and get/set attributes on the underlying request |
81 | 89 // object since Filters may wrap the request and change the value of |
| 90 // {@code GuiceFilter.getRequest()}. |
| 91 // |
| 92 // This _correctly_ throws up if the thread is out of scope. |
| 93 HttpServletRequest request = GuiceFilter.getOriginalRequest(); |
| 94 if (REQUEST_CONTEXT_KEYS.contains(key)) { |
| 95 // Don't store these keys as attributes, since they are handled by |
| 96 // GuiceFilter itself. |
| 97 return creator.get(); |
| 98 } |
82 synchronized (request) { | 99 synchronized (request) { |
83 Object obj = request.getAttribute(name); | 100 Object obj = request.getAttribute(name); |
84 if (NullObject.INSTANCE == obj) { | 101 if (NullObject.INSTANCE == obj) { |
85 return null; | 102 return null; |
86 } | 103 } |
87 @SuppressWarnings("unchecked") | 104 @SuppressWarnings("unchecked") |
88 T t = (T) obj; | 105 T t = (T) obj; |
89 if (t == null) { | 106 if (t == null) { |
90 t = creator.get(); | 107 t = creator.get(); |
91 request.setAttribute(name, (t != null) ? t : NullObject.INSTANCE); | 108 request.setAttribute(name, (t != null) ? t : NullObject.INSTANCE); |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 ···· | 192 ···· |
176 // Snapshot the seed map and add all the instances to our continuing HTTP re
quest. | 193 // Snapshot the seed map and add all the instances to our continuing HTTP re
quest. |
177 final ContinuingHttpServletRequest continuingRequest = | 194 final ContinuingHttpServletRequest continuingRequest = |
178 new ContinuingHttpServletRequest(GuiceFilter.getRequest()); | 195 new ContinuingHttpServletRequest(GuiceFilter.getRequest()); |
179 for (Map.Entry<Key<?>, Object> entry : seedMap.entrySet()) { | 196 for (Map.Entry<Key<?>, Object> entry : seedMap.entrySet()) { |
180 Object value = validateAndCanonicalizeValue(entry.getKey(), entry.getValue
()); | 197 Object value = validateAndCanonicalizeValue(entry.getKey(), entry.getValue
()); |
181 continuingRequest.setAttribute(entry.getKey().toString(), value); | 198 continuingRequest.setAttribute(entry.getKey().toString(), value); |
182 } | 199 } |
183 | 200 |
184 return new Callable<T>() { | 201 return new Callable<T>() { |
185 private HttpServletRequest request = continuingRequest; | 202 private final HttpServletRequest request = continuingRequest; |
186 | 203 |
187 public T call() throws Exception { | 204 public T call() throws Exception { |
188 GuiceFilter.Context context = GuiceFilter.localContext.get(); | 205 GuiceFilter.Context context = GuiceFilter.localContext.get(); |
189 Preconditions.checkState(null == context, | 206 Preconditions.checkState(null == context, |
190 "Cannot continue request in the same thread as a HTTP request!"); | 207 "Cannot continue request in the same thread as a HTTP request!"); |
191 | 208 |
192 // Only set up the request continuation if we're running in a | 209 // Only set up the request continuation if we're running in a |
193 // new vanilla thread. | 210 // new vanilla thread. |
194 GuiceFilter.localContext.set(new GuiceFilter.Context(request, null)); | 211 GuiceFilter.localContext.set(new GuiceFilter.Context(request, request, n
ull)); |
195 try { | 212 try { |
196 return callable.call(); | 213 return callable.call(); |
197 } finally { | 214 } finally { |
198 // Clear the copied context if we set one up. | 215 // Clear the copied context if we set one up. |
199 if (null == context) { | 216 if (null == context) { |
200 GuiceFilter.localContext.remove(); | 217 GuiceFilter.localContext.remove(); |
201 } | 218 } |
202 } | 219 } |
203 } | 220 } |
204 }; | 221 }; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 } | 285 } |
269 | 286 |
270 if (!key.getTypeLiteral().getRawType().isInstance(object)) { | 287 if (!key.getTypeLiteral().getRawType().isInstance(object)) { |
271 throw new IllegalArgumentException("Value[" + object + "] of type[" | 288 throw new IllegalArgumentException("Value[" + object + "] of type[" |
272 + object.getClass().getName() + "] is not compatible with key[" + key
+ "]"); | 289 + object.getClass().getName() + "] is not compatible with key[" + key
+ "]"); |
273 } | 290 } |
274 | 291 |
275 return object; | 292 return object; |
276 } | 293 } |
277 } | 294 } |
OLD | NEW |