LCOV - code coverage report
Current view: top level - server/plugins - AutoRegisterModules.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 34 121 28.1 %
Date: 2022-11-19 15:00:39 Functions: 5 11 45.5 %

          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.webui.JavaScriptPlugin.STATIC_INIT_JS;
      18             : import static com.google.gerrit.server.plugins.AutoRegisterUtil.calculateBindAnnotation;
      19             : import static com.google.gerrit.server.plugins.PluginGuiceEnvironment.is;
      20             : 
      21             : import com.google.common.collect.LinkedListMultimap;
      22             : import com.google.common.collect.ListMultimap;
      23             : import com.google.common.flogger.FluentLogger;
      24             : import com.google.gerrit.extensions.annotations.Export;
      25             : import com.google.gerrit.extensions.annotations.ExtensionPoint;
      26             : import com.google.gerrit.extensions.annotations.Listen;
      27             : import com.google.gerrit.extensions.registration.DynamicSet;
      28             : import com.google.gerrit.extensions.webui.JavaScriptPlugin;
      29             : import com.google.gerrit.extensions.webui.WebUiPlugin;
      30             : import com.google.gerrit.server.plugins.PluginContentScanner.ExtensionMetaData;
      31             : import com.google.inject.AbstractModule;
      32             : import com.google.inject.Module;
      33             : import com.google.inject.Scopes;
      34             : import com.google.inject.TypeLiteral;
      35             : import java.io.IOException;
      36             : import java.lang.annotation.Annotation;
      37             : import java.lang.reflect.ParameterizedType;
      38             : import java.util.Arrays;
      39             : import java.util.HashSet;
      40             : import java.util.Map;
      41             : import java.util.Set;
      42             : 
      43             : class AutoRegisterModules {
      44           2 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      45             : 
      46             :   private final String pluginName;
      47             :   private final PluginGuiceEnvironment env;
      48             :   private final PluginContentScanner scanner;
      49             :   private final ClassLoader classLoader;
      50             :   private final ModuleGenerator sshGen;
      51             :   private final ModuleGenerator httpGen;
      52             : 
      53             :   private Set<Class<?>> sysSingletons;
      54             :   private ListMultimap<TypeLiteral<?>, Class<?>> sysListen;
      55             :   private String initJs;
      56             : 
      57             :   Module sysModule;
      58             :   Module sshModule;
      59             :   Module httpModule;
      60             : 
      61             :   AutoRegisterModules(
      62             :       String pluginName,
      63             :       PluginGuiceEnvironment env,
      64             :       PluginContentScanner scanner,
      65           2 :       ClassLoader classLoader) {
      66           2 :     this.pluginName = pluginName;
      67           2 :     this.env = env;
      68           2 :     this.scanner = scanner;
      69           2 :     this.classLoader = classLoader;
      70           2 :     this.sshGen = env.hasSshModule() ? env.newSshModuleGenerator() : new ModuleGenerator.NOP();
      71           2 :     this.httpGen = env.hasHttpModule() ? env.newHttpModuleGenerator() : new ModuleGenerator.NOP();
      72           2 :   }
      73             : 
      74             :   AutoRegisterModules discover() throws InvalidPluginException {
      75           2 :     sysSingletons = new HashSet<>();
      76           2 :     sysListen = LinkedListMultimap.create();
      77           2 :     initJs = null;
      78             : 
      79           2 :     sshGen.setPluginName(pluginName);
      80           2 :     httpGen.setPluginName(pluginName);
      81             : 
      82           2 :     scan();
      83             : 
      84           2 :     if (!sysSingletons.isEmpty() || !sysListen.isEmpty() || initJs != null) {
      85           0 :       sysModule = makeSystemModule();
      86             :     }
      87           2 :     sshModule = sshGen.create();
      88           2 :     httpModule = httpGen.create();
      89           2 :     return this;
      90             :   }
      91             : 
      92             :   private Module makeSystemModule() {
      93           0 :     return new AbstractModule() {
      94             :       @Override
      95             :       protected void configure() {
      96           0 :         for (Class<?> clazz : sysSingletons) {
      97           0 :           bind(clazz).in(Scopes.SINGLETON);
      98           0 :         }
      99           0 :         for (Map.Entry<TypeLiteral<?>, Class<?>> e : sysListen.entries()) {
     100             :           @SuppressWarnings("unchecked")
     101           0 :           TypeLiteral<Object> type = (TypeLiteral<Object>) e.getKey();
     102             : 
     103             :           @SuppressWarnings("unchecked")
     104           0 :           Class<Object> impl = (Class<Object>) e.getValue();
     105             : 
     106           0 :           Annotation n = calculateBindAnnotation(impl);
     107           0 :           bind(type).annotatedWith(n).to(impl);
     108           0 :         }
     109           0 :         if (initJs != null) {
     110           0 :           DynamicSet.bind(binder(), WebUiPlugin.class).toInstance(new JavaScriptPlugin(initJs));
     111             :         }
     112           0 :       }
     113             :     };
     114             :   }
     115             : 
     116             :   private void scan() throws InvalidPluginException {
     117           2 :     Map<Class<? extends Annotation>, Iterable<ExtensionMetaData>> extensions =
     118           2 :         scanner.scan(pluginName, Arrays.asList(Export.class, Listen.class));
     119           2 :     for (ExtensionMetaData export : extensions.get(Export.class)) {
     120           1 :       export(export);
     121           1 :     }
     122           2 :     for (ExtensionMetaData listener : extensions.get(Listen.class)) {
     123           0 :       listen(listener);
     124           0 :     }
     125           2 :     if (env.hasHttpModule()) {
     126           0 :       exportInitJs();
     127             :     }
     128           2 :   }
     129             : 
     130             :   private void exportInitJs() {
     131             :     try {
     132           0 :       if (scanner.getEntry(STATIC_INIT_JS).isPresent()) {
     133           0 :         initJs = STATIC_INIT_JS;
     134             :       }
     135           0 :     } catch (IOException e) {
     136           0 :       logger.atWarning().withCause(e).log(
     137             :           "Cannot access %s from plugin %s: "
     138             :               + "JavaScript auto-discovered plugin will not be registered",
     139             :           STATIC_INIT_JS, pluginName);
     140           0 :     }
     141           0 :   }
     142             : 
     143             :   private void export(ExtensionMetaData def) throws InvalidPluginException {
     144             :     Class<?> clazz;
     145             :     try {
     146           1 :       clazz = Class.forName(def.className, false, classLoader);
     147           0 :     } catch (ClassNotFoundException err) {
     148           0 :       throw new InvalidPluginException(
     149           0 :           String.format("Cannot load %s with @Export(\"%s\")", def.className, def.annotationValue),
     150             :           err);
     151           1 :     }
     152             : 
     153           1 :     Export export = clazz.getAnnotation(Export.class);
     154           1 :     if (export == null) {
     155           0 :       logger.atWarning().log(
     156             :           "In plugin %s asm incorrectly parsed %s with @Export(\"%s\")",
     157           0 :           pluginName, clazz.getName(), def.annotationValue);
     158           0 :       return;
     159             :     }
     160             : 
     161           1 :     if (is("org.apache.sshd.server.command.Command", clazz)) {
     162           1 :       sshGen.export(export, clazz);
     163           0 :     } else if (is("javax.servlet.http.HttpServlet", clazz)) {
     164           0 :       httpGen.export(export, clazz);
     165           0 :       listen(clazz, clazz);
     166             :     } else {
     167           0 :       int cnt = sysListen.size();
     168           0 :       listen(clazz, clazz);
     169           0 :       if (cnt == sysListen.size()) {
     170             :         // If no bindings were recorded, the extension isn't recognized.
     171           0 :         throw new InvalidPluginException(
     172           0 :             String.format(
     173           0 :                 "Class %s with @Export(\"%s\") not supported", clazz.getName(), export.value()));
     174             :       }
     175             :     }
     176           1 :   }
     177             : 
     178             :   private void listen(ExtensionMetaData def) throws InvalidPluginException {
     179             :     Class<?> clazz;
     180             :     try {
     181           0 :       clazz = Class.forName(def.className, false, classLoader);
     182           0 :     } catch (ClassNotFoundException err) {
     183           0 :       throw new InvalidPluginException(
     184           0 :           String.format("Cannot load %s with @Listen", def.className), err);
     185           0 :     }
     186             : 
     187           0 :     Listen listen = clazz.getAnnotation(Listen.class);
     188           0 :     if (listen != null) {
     189           0 :       listen(clazz, clazz);
     190             :     } else {
     191           0 :       logger.atWarning().log(
     192           0 :           "In plugin %s asm incorrectly parsed %s with @Listen", pluginName, clazz.getName());
     193             :     }
     194           0 :   }
     195             : 
     196             :   private void listen(java.lang.reflect.Type type, Class<?> clazz) throws InvalidPluginException {
     197           0 :     while (type != null) {
     198             :       Class<?> rawType;
     199           0 :       if (type instanceof ParameterizedType) {
     200           0 :         rawType = (Class<?>) ((ParameterizedType) type).getRawType();
     201           0 :       } else if (type instanceof Class) {
     202           0 :         rawType = (Class<?>) type;
     203             :       } else {
     204           0 :         return;
     205             :       }
     206             : 
     207           0 :       if (rawType.getAnnotation(ExtensionPoint.class) != null) {
     208           0 :         TypeLiteral<?> tl = TypeLiteral.get(type);
     209           0 :         if (env.hasDynamicItem(tl)) {
     210           0 :           sysSingletons.add(clazz);
     211           0 :           sysListen.put(tl, clazz);
     212           0 :           httpGen.listen(tl, clazz);
     213           0 :           sshGen.listen(tl, clazz);
     214           0 :         } else if (env.hasDynamicSet(tl)) {
     215           0 :           sysSingletons.add(clazz);
     216           0 :           sysListen.put(tl, clazz);
     217           0 :           httpGen.listen(tl, clazz);
     218           0 :           sshGen.listen(tl, clazz);
     219           0 :         } else if (env.hasDynamicMap(tl)) {
     220           0 :           if (clazz.getAnnotation(Export.class) == null) {
     221           0 :             throw new InvalidPluginException(
     222           0 :                 String.format(
     223             :                     "Class %s requires @Export(\"name\") annotation for %s",
     224           0 :                     clazz.getName(), rawType.getName()));
     225             :           }
     226           0 :           sysSingletons.add(clazz);
     227           0 :           sysListen.put(tl, clazz);
     228           0 :           httpGen.listen(tl, clazz);
     229           0 :           sshGen.listen(tl, clazz);
     230             :         } else {
     231           0 :           throw new InvalidPluginException(
     232           0 :               String.format(
     233             :                   "Cannot register %s, server does not accept %s",
     234           0 :                   clazz.getName(), rawType.getName()));
     235             :         }
     236           0 :         return;
     237             :       }
     238             : 
     239           0 :       java.lang.reflect.Type[] interfaces = rawType.getGenericInterfaces();
     240           0 :       if (interfaces != null) {
     241           0 :         for (java.lang.reflect.Type i : interfaces) {
     242           0 :           listen(i, clazz);
     243             :         }
     244             :       }
     245             : 
     246           0 :       type = rawType.getGenericSuperclass();
     247           0 :     }
     248           0 :   }
     249             : }

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