LCOV - code coverage report
Current view: top level - server/plugins - PluginGuiceEnvironment.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 263 307 85.7 %
Date: 2022-11-19 15:00:39 Functions: 33 39 84.6 %

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

Generated by: LCOV version 1.16+git.20220603.dfeb750