LCOV - code coverage report
Current view: top level - server/plugins - JarPluginProvider.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 41 53 77.4 %
Date: 2022-11-19 15:00:39 Functions: 12 13 92.3 %

          Line data    Source code
       1             : // Copyright (C) 2014 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 com.google.common.base.MoreObjects;
      18             : import com.google.common.flogger.FluentLogger;
      19             : import com.google.gerrit.extensions.registration.PluginName;
      20             : import com.google.gerrit.server.config.PluginConfig;
      21             : import com.google.gerrit.server.config.PluginConfigFactory;
      22             : import com.google.gerrit.server.config.SitePaths;
      23             : import com.google.inject.Inject;
      24             : import java.io.IOException;
      25             : import java.io.InputStream;
      26             : import java.net.MalformedURLException;
      27             : import java.net.URL;
      28             : import java.net.URLClassLoader;
      29             : import java.nio.file.Files;
      30             : import java.nio.file.Path;
      31             : import java.nio.file.Paths;
      32             : import java.time.Instant;
      33             : import java.time.ZoneId;
      34             : import java.time.format.DateTimeFormatter;
      35             : import java.util.ArrayList;
      36             : import java.util.List;
      37             : import java.util.jar.JarFile;
      38             : import java.util.jar.Manifest;
      39             : import org.eclipse.jgit.internal.storage.file.FileSnapshot;
      40             : 
      41             : public class JarPluginProvider implements ServerPluginProvider {
      42           3 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      43             : 
      44             :   static final String PLUGIN_TMP_PREFIX = "plugin_";
      45             :   static final String JAR_EXTENSION = ".jar";
      46             : 
      47             :   private final Path tmpDir;
      48             :   private final PluginConfigFactory configFactory;
      49             : 
      50             :   @Inject
      51           3 :   JarPluginProvider(SitePaths sitePaths, PluginConfigFactory configFactory) {
      52           3 :     this.tmpDir = sitePaths.tmp_dir;
      53           3 :     this.configFactory = configFactory;
      54           3 :   }
      55             : 
      56             :   @Override
      57             :   public boolean handles(Path srcPath) {
      58           3 :     String fileName = srcPath.getFileName().toString();
      59           3 :     return fileName.endsWith(JAR_EXTENSION) || fileName.endsWith(JAR_EXTENSION + ".disabled");
      60             :   }
      61             : 
      62             :   @Override
      63             :   public String getPluginName(Path srcPath) {
      64             :     try {
      65           1 :       return MoreObjects.firstNonNull(getJarPluginName(srcPath), PluginUtil.nameOf(srcPath));
      66           0 :     } catch (IOException e) {
      67           0 :       throw new IllegalArgumentException(
      68             :           "Invalid plugin file " + srcPath + ": cannot get plugin name", e);
      69             :     }
      70             :   }
      71             : 
      72             :   public static String getJarPluginName(Path srcPath) throws IOException {
      73           1 :     try (JarFile jarFile = new JarFile(srcPath.toFile())) {
      74           1 :       return jarFile.getManifest().getMainAttributes().getValue("Gerrit-PluginName");
      75             :     }
      76             :   }
      77             : 
      78             :   @Override
      79             :   public ServerPlugin get(Path srcPath, FileSnapshot snapshot, PluginDescription description)
      80             :       throws InvalidPluginException {
      81             :     try {
      82           1 :       String name = getPluginName(srcPath);
      83           1 :       String extension = getExtension(srcPath);
      84           1 :       try (InputStream in = Files.newInputStream(srcPath)) {
      85           1 :         Path tmp = PluginUtil.asTemp(in, tempNameFor(name), extension, tmpDir);
      86           1 :         return loadJarPlugin(name, srcPath, snapshot, tmp, description);
      87             :       }
      88           0 :     } catch (IOException e) {
      89           0 :       throw new InvalidPluginException("Cannot load Jar plugin " + srcPath, e);
      90             :     }
      91             :   }
      92             : 
      93             :   @Override
      94             :   public String getProviderPluginName() {
      95           3 :     return PluginName.GERRIT;
      96             :   }
      97             : 
      98             :   private static String getExtension(Path path) {
      99           1 :     return getExtension(path.getFileName().toString());
     100             :   }
     101             : 
     102             :   private static String getExtension(String name) {
     103           1 :     int ext = name.lastIndexOf('.');
     104           1 :     return 0 < ext ? name.substring(ext) : "";
     105             :   }
     106             : 
     107             :   private static String tempNameFor(String name) {
     108           1 :     DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyMMdd_HHmm").withZone(ZoneId.of("UTC"));
     109           1 :     return PLUGIN_TMP_PREFIX + name + "_" + fmt.format(Instant.now()) + "_";
     110             :   }
     111             : 
     112             :   public static Path storeInTemp(String pluginName, InputStream in, SitePaths sitePaths)
     113             :       throws IOException {
     114           0 :     return PluginUtil.asTemp(in, tempNameFor(pluginName), ".jar", sitePaths.tmp_dir);
     115             :   }
     116             : 
     117             :   private ServerPlugin loadJarPlugin(
     118             :       String name, Path srcJar, FileSnapshot snapshot, Path tmp, PluginDescription description)
     119             :       throws IOException, InvalidPluginException, MalformedURLException {
     120           1 :     JarFile jarFile = new JarFile(tmp.toFile());
     121           1 :     boolean keep = false;
     122             :     try {
     123           1 :       Manifest manifest = jarFile.getManifest();
     124           1 :       Plugin.ApiType type = Plugin.getApiType(manifest);
     125             : 
     126           1 :       List<URL> urls = new ArrayList<>(2);
     127           1 :       String overlay = System.getProperty("gerrit.plugin-classes");
     128           1 :       if (overlay != null) {
     129           0 :         Path classes = Paths.get(overlay).resolve(name).resolve("main");
     130           0 :         if (Files.isDirectory(classes)) {
     131           0 :           logger.atInfo().log("plugin %s: including %s", name, classes);
     132           0 :           urls.add(classes.toUri().toURL());
     133             :         }
     134             :       }
     135           1 :       urls.add(tmp.toUri().toURL());
     136             : 
     137           1 :       ClassLoader pluginLoader =
     138           1 :           URLClassLoader.newInstance(
     139           1 :               urls.toArray(new URL[urls.size()]), PluginUtil.parentFor(type));
     140             : 
     141           1 :       JarScanner jarScanner = createJarScanner(tmp);
     142           1 :       PluginConfig pluginConfig = configFactory.getFromGerritConfig(name);
     143             : 
     144           1 :       ServerPlugin plugin =
     145             :           new ServerPlugin(
     146             :               name,
     147             :               description.canonicalUrl,
     148             :               description.user,
     149             :               srcJar,
     150             :               snapshot,
     151             :               jarScanner,
     152             :               description.dataDir,
     153             :               pluginLoader,
     154           1 :               pluginConfig.getString("metricsPrefix", null),
     155             :               description.gerritRuntime);
     156           1 :       plugin.setCleanupHandle(new CleanupHandle(tmp, jarFile));
     157           1 :       keep = true;
     158           1 :       return plugin;
     159             :     } finally {
     160           1 :       if (!keep) {
     161           0 :         jarFile.close();
     162             :       }
     163             :     }
     164             :   }
     165             : 
     166             :   private JarScanner createJarScanner(Path srcJar) throws InvalidPluginException {
     167             :     try {
     168           1 :       return new JarScanner(srcJar);
     169           0 :     } catch (IOException e) {
     170           0 :       throw new InvalidPluginException("Cannot scan plugin file " + srcJar, e);
     171             :     }
     172             :   }
     173             : }

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