// Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.gerrit.pgm.init; import com.google.common.collect.FluentIterable; import com.google.gerrit.common.PluginData; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitFlags; import com.google.gerrit.pgm.init.api.InitStep; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.plugins.JarPluginProvider; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Singleton; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; @Singleton public class InitPlugins implements InitStep { public static final String PLUGIN_DIR = "WEB-INF/plugins/"; public static final String JAR = ".jar"; public static List<PluginData> listPlugins( SitePaths site, PluginsDistribution pluginsDistribution) throws IOException { return listPlugins(site, false, pluginsDistribution); } public static List<PluginData> listPluginsAndRemoveTempFiles( SitePaths site, PluginsDistribution pluginsDistribution) throws IOException { return listPlugins(site, true, pluginsDistribution); } private static List<PluginData> listPlugins( final SitePaths site, final boolean deleteTempPluginFile, PluginsDistribution pluginsDistribution) throws IOException { final List<PluginData> result = new ArrayList<>(); pluginsDistribution.foreach( new PluginsDistribution.Processor() { @Override public void process(String pluginName, InputStream in) throws IOException { Path tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site); String pluginVersion = getVersion(tmpPlugin); if (deleteTempPluginFile) { Files.delete(tmpPlugin); } result.add(new PluginData(pluginName, pluginVersion, tmpPlugin)); } }); return FluentIterable.from(result) .toSortedList( new Comparator<PluginData>() { @Override public int compare(PluginData a, PluginData b) { return a.name.compareTo(b.name); } }); } private final ConsoleUI ui; private final SitePaths site; private final InitFlags initFlags; private final InitPluginStepsLoader pluginLoader; private final PluginsDistribution pluginsDistribution; private Injector postRunInjector; @Inject InitPlugins( final ConsoleUI ui, final SitePaths site, InitFlags initFlags, InitPluginStepsLoader pluginLoader, PluginsDistribution pluginsDistribution) { this.ui = ui; this.site = site; this.initFlags = initFlags; this.pluginLoader = pluginLoader; this.pluginsDistribution = pluginsDistribution; } @Override public void run() throws Exception { ui.header("Plugins"); installPlugins(); initPlugins(); } @Override public void postRun() throws Exception { postInitPlugins(); } @Inject(optional = true) void setPostRunInjector(Injector injector) { postRunInjector = injector; } private void installPlugins() throws IOException { ui.message("Installing plugins.\n"); List<PluginData> plugins = listPlugins(site, pluginsDistribution); for (PluginData plugin : plugins) { String pluginName = plugin.name; try { final Path tmpPlugin = plugin.pluginPath; Path p = site.plugins_dir.resolve(plugin.name + ".jar"); boolean upgrade = Files.exists(p); if (!(initFlags.installPlugins.contains(pluginName) || initFlags.installAllPlugins || ui.yesno(upgrade, "Install plugin %s version %s", pluginName, plugin.version))) { Files.deleteIfExists(tmpPlugin); continue; } if (upgrade) { final String installedPluginVersion = getVersion(p); if (!ui.yesno( upgrade, "%s %s is already installed, overwrite it", plugin.name, installedPluginVersion)) { Files.deleteIfExists(tmpPlugin); continue; } try { Files.delete(p); } catch (IOException e) { throw new IOException( "Failed to delete plugin " + pluginName + ": " + p.toAbsolutePath(), e); } } try { Files.move(tmpPlugin, p); if (upgrade) { // or update that is not an upgrade ui.message("Updated %s to %s\n", plugin.name, plugin.version); } else { ui.message("Installed %s %s\n", plugin.name, plugin.version); } } catch (IOException e) { throw new IOException( "Failed to install plugin " + pluginName + ": " + tmpPlugin.toAbsolutePath() + " -> " + p.toAbsolutePath(), e); } } finally { Files.deleteIfExists(plugin.pluginPath); } } if (plugins.isEmpty()) { ui.message("No plugins found to install.\n"); } } private void initPlugins() throws Exception { ui.message("Initializing plugins.\n"); Collection<InitStep> initSteps = pluginLoader.getInitSteps(); if (initSteps.isEmpty()) { ui.message("No plugins found with init steps.\n"); } else { for (InitStep initStep : initSteps) { initStep.run(); } } } private void postInitPlugins() throws Exception { for (InitStep initStep : pluginLoader.getInitSteps()) { postRunInjector.injectMembers(initStep); initStep.postRun(); } } private static String getVersion(Path plugin) throws IOException { try (JarFile jarFile = new JarFile(plugin.toFile())) { Manifest manifest = jarFile.getManifest(); Attributes main = manifest.getMainAttributes(); return main.getValue(Attributes.Name.IMPLEMENTATION_VERSION); } } }