/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
Java | 648 lines | 525 code | 80 blank | 43 comment | 125 complexity | c676a774f45fdff394da89905a1c22b9 MD5 | raw file
1// Copyright (C) 2012 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gerrit.server.plugins;
16
17import static com.google.common.base.Preconditions.checkNotNull;
18import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicItemsOf;
19import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicMapsOf;
20import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.dynamicSetsOf;
21
22import com.google.common.collect.LinkedListMultimap;
23import com.google.common.collect.ListMultimap;
24import com.google.common.collect.Lists;
25import com.google.gerrit.common.Nullable;
26import com.google.gerrit.extensions.annotations.RootRelative;
27import com.google.gerrit.extensions.events.LifecycleListener;
28import com.google.gerrit.extensions.registration.DynamicItem;
29import com.google.gerrit.extensions.registration.DynamicMap;
30import com.google.gerrit.extensions.registration.DynamicSet;
31import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
32import com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes;
33import com.google.gerrit.extensions.registration.RegistrationHandle;
34import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
35import com.google.gerrit.extensions.systemstatus.ServerInformation;
36import com.google.gerrit.extensions.webui.WebUiPlugin;
37import com.google.gerrit.index.IndexCollection;
38import com.google.gerrit.metrics.MetricMaker;
39import com.google.gerrit.server.util.PluginRequestContext;
40import com.google.gerrit.server.util.RequestContext;
41import com.google.gerrit.server.util.ThreadLocalRequestContext;
42import com.google.inject.AbstractModule;
43import com.google.inject.Binding;
44import com.google.inject.Guice;
45import com.google.inject.Inject;
46import com.google.inject.Injector;
47import com.google.inject.Key;
48import com.google.inject.Module;
49import com.google.inject.Provider;
50import com.google.inject.Singleton;
51import com.google.inject.TypeLiteral;
52import com.google.inject.internal.UniqueAnnotations;
53import java.lang.annotation.Annotation;
54import java.lang.reflect.ParameterizedType;
55import java.util.Collections;
56import java.util.HashMap;
57import java.util.HashSet;
58import java.util.Iterator;
59import java.util.LinkedHashMap;
60import java.util.List;
61import java.util.Map;
62import java.util.Set;
63import java.util.concurrent.CopyOnWriteArrayList;
64import javax.servlet.http.HttpServletRequest;
65import javax.servlet.http.HttpServletResponse;
66
67/**
68 * Tracks Guice bindings that should be exposed to loaded plugins.
69 *
70 * <p>This is an internal implementation detail of how the main server is able to export its
71 * explicit Guice bindings to tightly coupled plugins, giving them access to singletons and request
72 * scoped resources just like any core code.
73 */
74@Singleton
75public class PluginGuiceEnvironment {
76 private final Injector sysInjector;
77 private final ServerInformation srvInfo;
78 private final ThreadLocalRequestContext local;
79 private final CopyConfigModule copyConfigModule;
80 private final Set<Key<?>> copyConfigKeys;
81 private final List<StartPluginListener> onStart;
82 private final List<StopPluginListener> onStop;
83 private final List<ReloadPluginListener> onReload;
84 private final MetricMaker serverMetrics;
85
86 private Module sysModule;
87 private Module sshModule;
88 private Module httpModule;
89
90 private Provider<ModuleGenerator> sshGen;
91 private Provider<ModuleGenerator> httpGen;
92
93 private Map<TypeLiteral<?>, DynamicItem<?>> sysItems;
94 private Map<TypeLiteral<?>, DynamicItem<?>> sshItems;
95 private Map<TypeLiteral<?>, DynamicItem<?>> httpItems;
96
97 private Map<TypeLiteral<?>, DynamicSet<?>> sysSets;
98 private Map<TypeLiteral<?>, DynamicSet<?>> sshSets;
99 private Map<TypeLiteral<?>, DynamicSet<?>> httpSets;
100
101 private Map<TypeLiteral<?>, DynamicMap<?>> sysMaps;
102 private Map<TypeLiteral<?>, DynamicMap<?>> sshMaps;
103 private Map<TypeLiteral<?>, DynamicMap<?>> httpMaps;
104
105 @Inject
106 PluginGuiceEnvironment(
107 Injector sysInjector,
108 ThreadLocalRequestContext local,
109 ServerInformation srvInfo,
110 CopyConfigModule ccm,
111 MetricMaker serverMetrics) {
112 this.sysInjector = sysInjector;
113 this.srvInfo = srvInfo;
114 this.local = local;
115 this.copyConfigModule = ccm;
116 this.copyConfigKeys = Guice.createInjector(ccm).getAllBindings().keySet();
117 this.serverMetrics = serverMetrics;
118
119 onStart = new CopyOnWriteArrayList<>();
120 onStart.addAll(listeners(sysInjector, StartPluginListener.class));
121
122 onStop = new CopyOnWriteArrayList<>();
123 onStop.addAll(listeners(sysInjector, StopPluginListener.class));
124
125 onReload = new CopyOnWriteArrayList<>();
126 onReload.addAll(listeners(sysInjector, ReloadPluginListener.class));
127
128 sysItems = dynamicItemsOf(sysInjector);
129 sysSets = dynamicSetsOf(sysInjector);
130 sysMaps = dynamicMapsOf(sysInjector);
131 }
132
133 ServerInformation getServerInformation() {
134 return srvInfo;
135 }
136
137 MetricMaker getServerMetrics() {
138 return serverMetrics;
139 }
140
141 boolean hasDynamicItem(TypeLiteral<?> type) {
142 return sysItems.containsKey(type)
143 || (sshItems != null && sshItems.containsKey(type))
144 || (httpItems != null && httpItems.containsKey(type));
145 }
146
147 boolean hasDynamicSet(TypeLiteral<?> type) {
148 return sysSets.containsKey(type)
149 || (sshSets != null && sshSets.containsKey(type))
150 || (httpSets != null && httpSets.containsKey(type));
151 }
152
153 boolean hasDynamicMap(TypeLiteral<?> type) {
154 return sysMaps.containsKey(type)
155 || (sshMaps != null && sshMaps.containsKey(type))
156 || (httpMaps != null && httpMaps.containsKey(type));
157 }
158
159 public Module getSysModule() {
160 return sysModule;
161 }
162
163 public void setDbCfgInjector(Injector dbInjector, Injector cfgInjector) {
164 final Module db = copy(dbInjector);
165 final Module cm = copy(cfgInjector);
166 final Module sm = copy(sysInjector);
167 sysModule =
168 new AbstractModule() {
169 @Override
170 protected void configure() {
171 install(copyConfigModule);
172 install(db);
173 install(cm);
174 install(sm);
175 }
176 };
177 }
178
179 public void setSshInjector(Injector injector) {
180 sshModule = copy(injector);
181 sshGen = injector.getProvider(ModuleGenerator.class);
182 sshItems = dynamicItemsOf(injector);
183 sshSets = dynamicSetsOf(injector);
184 sshMaps = dynamicMapsOf(injector);
185 onStart.addAll(listeners(injector, StartPluginListener.class));
186 onStop.addAll(listeners(injector, StopPluginListener.class));
187 onReload.addAll(listeners(injector, ReloadPluginListener.class));
188 }
189
190 boolean hasSshModule() {
191 return sshModule != null;
192 }
193
194 Module getSshModule() {
195 return sshModule;
196 }
197
198 ModuleGenerator newSshModuleGenerator() {
199 return sshGen.get();
200 }
201
202 public void setHttpInjector(Injector injector) {
203 httpModule = copy(injector);
204 httpGen = injector.getProvider(ModuleGenerator.class);
205 httpItems = dynamicItemsOf(injector);
206 httpSets = httpDynamicSetsOf(injector);
207 httpMaps = dynamicMapsOf(injector);
208 onStart.addAll(listeners(injector, StartPluginListener.class));
209 onStop.addAll(listeners(injector, StopPluginListener.class));
210 onReload.addAll(listeners(injector, ReloadPluginListener.class));
211 }
212
213 private Map<TypeLiteral<?>, DynamicSet<?>> httpDynamicSetsOf(Injector i) {
214 // Copy binding of DynamicSet<WebUiPlugin> from sysInjector to HTTP.
215 // This supports older plugins that bound a plugin in the HttpModule.
216 TypeLiteral<WebUiPlugin> key = TypeLiteral.get(WebUiPlugin.class);
217 DynamicSet<?> web = sysSets.get(key);
218 checkNotNull(web, "DynamicSet<WebUiPlugin> exists in sysInjector");
219
220 Map<TypeLiteral<?>, DynamicSet<?>> m = new HashMap<>(dynamicSetsOf(i));
221 m.put(key, web);
222 return Collections.unmodifiableMap(m);
223 }
224
225 boolean hasHttpModule() {
226 return httpModule != null;
227 }
228
229 Module getHttpModule() {
230 return httpModule;
231 }
232
233 ModuleGenerator newHttpModuleGenerator() {
234 return httpGen.get();
235 }
236
237 public RequestContext enter(Plugin plugin) {
238 return local.setContext(new PluginRequestContext(plugin.getPluginUser()));
239 }
240
241 public void exit(RequestContext old) {
242 local.setContext(old);
243 }
244
245 public void onStartPlugin(Plugin plugin) {
246 RequestContext oldContext = enter(plugin);
247 try {
248 attachItem(sysItems, plugin.getSysInjector(), plugin);
249 attachItem(sshItems, plugin.getSshInjector(), plugin);
250 attachItem(httpItems, plugin.getHttpInjector(), plugin);
251
252 attachSet(sysSets, plugin.getSysInjector(), plugin);
253 attachSet(sshSets, plugin.getSshInjector(), plugin);
254 attachSet(httpSets, plugin.getHttpInjector(), plugin);
255
256 attachMap(sysMaps, plugin.getSysInjector(), plugin);
257 attachMap(sshMaps, plugin.getSshInjector(), plugin);
258 attachMap(httpMaps, plugin.getHttpInjector(), plugin);
259 } finally {
260 exit(oldContext);
261 }
262
263 for (StartPluginListener l : onStart) {
264 l.onStartPlugin(plugin);
265 }
266 }
267
268 public void onStopPlugin(Plugin plugin) {
269 for (StopPluginListener l : onStop) {
270 l.onStopPlugin(plugin);
271 }
272 }
273
274 private void attachItem(
275 Map<TypeLiteral<?>, DynamicItem<?>> items, @Nullable Injector src, Plugin plugin) {
276 for (RegistrationHandle h :
277 PrivateInternals_DynamicTypes.attachItems(src, items, plugin.getName())) {
278 plugin.add(h);
279 }
280 }
281
282 private void attachSet(
283 Map<TypeLiteral<?>, DynamicSet<?>> sets, @Nullable Injector src, Plugin plugin) {
284 for (RegistrationHandle h : PrivateInternals_DynamicTypes.attachSets(src, sets)) {
285 plugin.add(h);
286 }
287 }
288
289 private void attachMap(
290 Map<TypeLiteral<?>, DynamicMap<?>> maps, @Nullable Injector src, Plugin plugin) {
291 for (RegistrationHandle h :
292 PrivateInternals_DynamicTypes.attachMaps(src, plugin.getName(), maps)) {
293 plugin.add(h);
294 }
295 }
296
297 void onReloadPlugin(Plugin oldPlugin, Plugin newPlugin) {
298 // Index all old registrations by the raw type. These may be replaced
299 // during the reattach calls below. Any that are not replaced will be
300 // removed when the old plugin does its stop routine.
301 ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> old = LinkedListMultimap.create();
302 for (ReloadableRegistrationHandle<?> h : oldPlugin.getReloadableHandles()) {
303 old.put(h.getKey().getTypeLiteral(), h);
304 }
305
306 RequestContext oldContext = enter(newPlugin);
307 try {
308 reattachMap(old, sysMaps, newPlugin.getSysInjector(), newPlugin);
309 reattachMap(old, sshMaps, newPlugin.getSshInjector(), newPlugin);
310 reattachMap(old, httpMaps, newPlugin.getHttpInjector(), newPlugin);
311
312 reattachSet(old, sysSets, newPlugin.getSysInjector(), newPlugin);
313 reattachSet(old, sshSets, newPlugin.getSshInjector(), newPlugin);
314 reattachSet(old, httpSets, newPlugin.getHttpInjector(), newPlugin);
315
316 reattachItem(old, sysItems, newPlugin.getSysInjector(), newPlugin);
317 reattachItem(old, sshItems, newPlugin.getSshInjector(), newPlugin);
318 reattachItem(old, httpItems, newPlugin.getHttpInjector(), newPlugin);
319 } finally {
320 exit(oldContext);
321 }
322
323 for (ReloadPluginListener l : onReload) {
324 l.onReloadPlugin(oldPlugin, newPlugin);
325 }
326 }
327
328 private void reattachMap(
329 ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
330 Map<TypeLiteral<?>, DynamicMap<?>> maps,
331 @Nullable Injector src,
332 Plugin newPlugin) {
333 if (src == null || maps == null || maps.isEmpty()) {
334 return;
335 }
336
337 for (Map.Entry<TypeLiteral<?>, DynamicMap<?>> e : maps.entrySet()) {
338 @SuppressWarnings("unchecked")
339 TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
340
341 @SuppressWarnings("unchecked")
342 PrivateInternals_DynamicMapImpl<Object> map =
343 (PrivateInternals_DynamicMapImpl<Object>) e.getValue();
344
345 Map<Annotation, ReloadableRegistrationHandle<?>> am = new HashMap<>();
346 for (ReloadableRegistrationHandle<?> h : oldHandles.get(type)) {
347 Annotation a = h.getKey().getAnnotation();
348 if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
349 am.put(a, h);
350 }
351 }
352
353 for (Binding<?> binding : bindings(src, e.getKey())) {
354 @SuppressWarnings("unchecked")
355 Binding<Object> b = (Binding<Object>) binding;
356 Key<Object> key = b.getKey();
357 if (key.getAnnotation() == null) {
358 continue;
359 }
360
361 @SuppressWarnings("unchecked")
362 ReloadableRegistrationHandle<Object> h =
363 (ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
364 if (h != null) {
365 replace(newPlugin, h, b);
366 oldHandles.remove(type, h);
367 } else {
368 newPlugin.add(map.put(newPlugin.getName(), b.getKey(), b.getProvider()));
369 }
370 }
371 }
372 }
373
374 /** Type used to declare unique annotations. Guice hides this, so extract it. */
375 private static final Class<?> UNIQUE_ANNOTATION = UniqueAnnotations.create().annotationType();
376
377 private void reattachSet(
378 ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
379 Map<TypeLiteral<?>, DynamicSet<?>> sets,
380 @Nullable Injector src,
381 Plugin newPlugin) {
382 if (src == null || sets == null || sets.isEmpty()) {
383 return;
384 }
385
386 for (Map.Entry<TypeLiteral<?>, DynamicSet<?>> e : sets.entrySet()) {
387 @SuppressWarnings("unchecked")
388 TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
389
390 @SuppressWarnings("unchecked")
391 DynamicSet<Object> set = (DynamicSet<Object>) e.getValue();
392
393 // Index all old handles that match this DynamicSet<T> keyed by
394 // annotations. Ignore the unique annotations, thereby favoring
395 // the @Named annotations or some other non-unique naming.
396 Map<Annotation, ReloadableRegistrationHandle<?>> am = new HashMap<>();
397 List<ReloadableRegistrationHandle<?>> old = oldHandles.get(type);
398 Iterator<ReloadableRegistrationHandle<?>> oi = old.iterator();
399 while (oi.hasNext()) {
400 ReloadableRegistrationHandle<?> h = oi.next();
401 Annotation a = h.getKey().getAnnotation();
402 if (a != null && !UNIQUE_ANNOTATION.isInstance(a)) {
403 am.put(a, h);
404 oi.remove();
405 }
406 }
407
408 // Replace old handles with new bindings, favoring cases where there
409 // is an exact match on an @Named annotation. If there is no match
410 // pick any handle and replace it. We generally expect only one
411 // handle of each DynamicSet type when using unique annotations, but
412 // possibly multiple ones if @Named was used. Plugin authors that want
413 // atomic replacement across reloads should use @Named annotations with
414 // stable names that do not change across plugin versions to ensure the
415 // handles are swapped correctly.
416 oi = old.iterator();
417 for (Binding<?> binding : bindings(src, type)) {
418 @SuppressWarnings("unchecked")
419 Binding<Object> b = (Binding<Object>) binding;
420 Key<Object> key = b.getKey();
421 if (key.getAnnotation() == null) {
422 continue;
423 }
424
425 @SuppressWarnings("unchecked")
426 ReloadableRegistrationHandle<Object> h1 =
427 (ReloadableRegistrationHandle<Object>) am.remove(key.getAnnotation());
428 if (h1 != null) {
429 replace(newPlugin, h1, b);
430 } else if (oi.hasNext()) {
431 @SuppressWarnings("unchecked")
432 ReloadableRegistrationHandle<Object> h2 =
433 (ReloadableRegistrationHandle<Object>) oi.next();
434 oi.remove();
435 replace(newPlugin, h2, b);
436 } else {
437 newPlugin.add(set.add(b.getKey(), b.getProvider()));
438 }
439 }
440 }
441 }
442
443 private void reattachItem(
444 ListMultimap<TypeLiteral<?>, ReloadableRegistrationHandle<?>> oldHandles,
445 Map<TypeLiteral<?>, DynamicItem<?>> items,
446 @Nullable Injector src,
447 Plugin newPlugin) {
448 if (src == null || items == null || items.isEmpty()) {
449 return;
450 }
451
452 for (Map.Entry<TypeLiteral<?>, DynamicItem<?>> e : items.entrySet()) {
453 @SuppressWarnings("unchecked")
454 TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
455
456 @SuppressWarnings("unchecked")
457 DynamicItem<Object> item = (DynamicItem<Object>) e.getValue();
458
459 Iterator<ReloadableRegistrationHandle<?>> oi = oldHandles.get(type).iterator();
460
461 for (Binding<?> binding : bindings(src, type)) {
462 @SuppressWarnings("unchecked")
463 Binding<Object> b = (Binding<Object>) binding;
464 if (oi.hasNext()) {
465 @SuppressWarnings("unchecked")
466 ReloadableRegistrationHandle<Object> h = (ReloadableRegistrationHandle<Object>) oi.next();
467 oi.remove();
468 replace(newPlugin, h, b);
469 } else {
470 newPlugin.add(item.set(b.getKey(), b.getProvider(), newPlugin.getName()));
471 }
472 }
473 }
474 }
475
476 private static <T> void replace(
477 Plugin newPlugin, ReloadableRegistrationHandle<T> h, Binding<T> b) {
478 RegistrationHandle n = h.replace(b.getKey(), b.getProvider());
479 if (n != null) {
480 newPlugin.add(n);
481 }
482 }
483
484 static <T> List<T> listeners(Injector src, Class<T> type) {
485 List<Binding<T>> bindings = bindings(src, TypeLiteral.get(type));
486 int cnt = bindings != null ? bindings.size() : 0;
487 List<T> found = Lists.newArrayListWithCapacity(cnt);
488 if (bindings != null) {
489 for (Binding<T> b : bindings) {
490 found.add(b.getProvider().get());
491 }
492 }
493 return found;
494 }
495
496 private static <T> List<Binding<T>> bindings(Injector src, TypeLiteral<T> type) {
497 return src.findBindingsByType(type);
498 }
499
500 private Module copy(Injector src) {
501 Set<TypeLiteral<?>> dynamicTypes = new HashSet<>();
502 Set<TypeLiteral<?>> dynamicItemTypes = new HashSet<>();
503 for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
504 TypeLiteral<?> type = e.getKey().getTypeLiteral();
505 if (type.getRawType() == DynamicItem.class) {
506 ParameterizedType t = (ParameterizedType) type.getType();
507 dynamicItemTypes.add(TypeLiteral.get(t.getActualTypeArguments()[0]));
508 } else if (type.getRawType() == DynamicSet.class || type.getRawType() == DynamicMap.class) {
509 ParameterizedType t = (ParameterizedType) type.getType();
510 dynamicTypes.add(TypeLiteral.get(t.getActualTypeArguments()[0]));
511 }
512 }
513
514 final Map<Key<?>, Binding<?>> bindings = new LinkedHashMap<>();
515 for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
516 if (dynamicTypes.contains(e.getKey().getTypeLiteral())
517 && e.getKey().getAnnotation() != null) {
518 // A type used in DynamicSet or DynamicMap that has an annotation
519 // must be picked up by the set/map itself. A type used in either
520 // but without an annotation may be magic glue implementing F and
521 // using DynamicSet<F> or DynamicMap<F> internally. That should be
522 // exported to plugins.
523 continue;
524 } else if (dynamicItemTypes.contains(e.getKey().getTypeLiteral())) {
525 continue;
526 } else if (shouldCopy(e.getKey())) {
527 bindings.put(e.getKey(), e.getValue());
528 }
529 }
530 bindings.remove(Key.get(Injector.class));
531 bindings.remove(Key.get(java.util.logging.Logger.class));
532
533 @Nullable
534 final Binding<HttpServletRequest> requestBinding =
535 src.getExistingBinding(Key.get(HttpServletRequest.class));
536
537 @Nullable
538 final Binding<HttpServletResponse> responseBinding =
539 src.getExistingBinding(Key.get(HttpServletResponse.class));
540
541 return new AbstractModule() {
542 @SuppressWarnings("unchecked")
543 @Override
544 protected void configure() {
545 for (Map.Entry<Key<?>, Binding<?>> e : bindings.entrySet()) {
546 Key<Object> k = (Key<Object>) e.getKey();
547 Binding<Object> b = (Binding<Object>) e.getValue();
548 bind(k).toProvider(b.getProvider());
549 }
550
551 if (requestBinding != null) {
552 bind(HttpServletRequest.class)
553 .annotatedWith(RootRelative.class)
554 .toProvider(requestBinding.getProvider());
555 }
556 if (responseBinding != null) {
557 bind(HttpServletResponse.class)
558 .annotatedWith(RootRelative.class)
559 .toProvider(responseBinding.getProvider());
560 }
561 }
562 };
563 }
564
565 private boolean shouldCopy(Key<?> key) {
566 if (copyConfigKeys.contains(key)) {
567 return false;
568 }
569 Class<?> type = key.getTypeLiteral().getRawType();
570 if (LifecycleListener.class.isAssignableFrom(type)
571 // This is needed for secondary index to work from plugin listeners
572 && !IndexCollection.class.isAssignableFrom(type)) {
573 return false;
574 }
575 if (StartPluginListener.class.isAssignableFrom(type)) {
576 return false;
577 }
578 if (StopPluginListener.class.isAssignableFrom(type)) {
579 return false;
580 }
581 if (MetricMaker.class.isAssignableFrom(type)) {
582 return false;
583 }
584
585 if (type.getName().startsWith("com.google.inject.")) {
586 return false;
587 }
588
589 if (is("org.apache.sshd.server.Command", type)) {
590 return false;
591 }
592
593 if (is("javax.servlet.Filter", type)) {
594 return false;
595 }
596 if (is("javax.servlet.ServletContext", type)) {
597 return false;
598 }
599 if (is("javax.servlet.ServletRequest", type)) {
600 return false;
601 }
602 if (is("javax.servlet.ServletResponse", type)) {
603 return false;
604 }
605 if (is("javax.servlet.http.HttpServlet", type)) {
606 return false;
607 }
608 if (is("javax.servlet.http.HttpServletRequest", type)) {
609 return false;
610 }
611 if (is("javax.servlet.http.HttpServletResponse", type)) {
612 return false;
613 }
614 if (is("javax.servlet.http.HttpSession", type)) {
615 return false;
616 }
617 if (Map.class.isAssignableFrom(type)
618 && key.getAnnotationType() != null
619 && "com.google.inject.servlet.RequestParameters"
620 .equals(key.getAnnotationType().getName())) {
621 return false;
622 }
623 if (type.getName().startsWith("com.google.gerrit.httpd.GitOverHttpServlet$")) {
624 return false;
625 }
626 return true;
627 }
628
629 static boolean is(String name, Class<?> type) {
630 while (type != null) {
631 if (name.equals(type.getName())) {
632 return true;
633 }
634
635 Class<?>[] interfaces = type.getInterfaces();
636 if (interfaces != null) {
637 for (Class<?> i : interfaces) {
638 if (is(name, i)) {
639 return true;
640 }
641 }
642 }
643
644 type = type.getSuperclass();
645 }
646 return false;
647 }
648}