PageRenderTime 46ms CodeModel.GetById 13ms app.highlight 28ms RepoModel.GetById 0ms app.codeStats 0ms

/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java

https://gitlab.com/chenfengxu/gerrit
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}