From 43777f1e27ec34d4b511e8b99917406060dfe592 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 21 Jun 2019 08:32:58 +0200 Subject: [PATCH] move bootstrapping of plugins into own class PluginBootstrap --- .../scm/boot/BootstrapContextListener.java | 318 +----------------- .../java/sonia/scm/boot/PluginBootstrap.java | 208 ++++++++++++ 2 files changed, 222 insertions(+), 304 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/boot/PluginBootstrap.java diff --git a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java index 572ff99d49..da67e30f81 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java @@ -29,50 +29,25 @@ package sonia.scm.boot; -import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableList; -import com.google.common.io.Files; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.assistedinject.FactoryModuleBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import sonia.scm.EagerSingletonModule; import sonia.scm.SCMContext; import sonia.scm.ScmContextListener; import sonia.scm.ScmEventBusModule; import sonia.scm.ScmInitializerModule; -import sonia.scm.migration.UpdateException; -import sonia.scm.plugin.DefaultPluginLoader; -import sonia.scm.plugin.Plugin; -import sonia.scm.plugin.PluginException; -import sonia.scm.plugin.PluginLoadException; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginWrapper; -import sonia.scm.plugin.PluginsInternal; -import sonia.scm.plugin.SmpArchive; import sonia.scm.update.MigrationWizardContextListener; import sonia.scm.update.UpdateEngine; -import sonia.scm.util.IOUtil; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpServletResponse; -import javax.xml.bind.DataBindingException; -import javax.xml.bind.JAXB; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Iterator; -import java.util.List; import java.util.Set; /** @@ -81,47 +56,12 @@ import java.util.Set; */ public class BootstrapContextListener implements ServletContextListener { - /** Field description */ - private static final String DIRECTORY_PLUGINS = "plugins"; - - /** Field description */ - private static final String PLUGIN_DIRECTORY = "/WEB-INF/plugins/"; - - /** - * the logger for BootstrapContextListener - */ - private static final Logger logger = - LoggerFactory.getLogger(BootstrapContextListener.class); - - /** Field description */ - private static final String PLUGIN_COREINDEX = - PLUGIN_DIRECTORY.concat("plugin-index.xml"); - - //~--- methods -------------------------------------------------------------- - private final ClassLoaderLifeCycle classLoaderLifeCycle = ClassLoaderLifeCycle.create(); - /** - * Method description - * - * - * @param sce - */ - @Override - public void contextDestroyed(ServletContextEvent sce) { - contextListener.contextDestroyed(sce); - classLoaderLifeCycle.shutdown(); - context = null; - contextListener = null; - } + private ServletContext context; + private ServletContextListener contextListener; - /** - * Method description - * - * - * @param sce - */ @Override public void contextInitialized(ServletContextEvent sce) { classLoaderLifeCycle.init(); @@ -133,6 +73,15 @@ public class BootstrapContextListener implements ServletContextListener { contextListener.contextInitialized(sce); } + @Override + public void contextDestroyed(ServletContextEvent sce) { + contextListener.contextDestroyed(sce); + classLoaderLifeCycle.shutdown(); + + context = null; + contextListener = null; + } + private void createContextListener() { Throwable startupError = SCMContext.getContext().getStartupError(); if (startupError != null) { @@ -146,32 +95,11 @@ public class BootstrapContextListener implements ServletContextListener { } private void createMigrationOrNormalContextListener() { - Set plugins; - PluginLoader pluginLoader; + PluginBootstrap pluginBootstrap = new PluginBootstrap(context, classLoaderLifeCycle); - try { - File pluginDirectory = getPluginDirectory(); + Injector bootstrapInjector = createBootstrapInjector(pluginBootstrap.getPluginLoader()); - renameOldPluginsFolder(pluginDirectory); - - if (!isCorePluginExtractionDisabled()) { - extractCorePlugins(context, pluginDirectory); - } else { - logger.info("core plugin extraction is disabled"); - } - - - plugins = PluginsInternal.collectPlugins(classLoaderLifeCycle, pluginDirectory.toPath()); - - pluginLoader = new DefaultPluginLoader(context, classLoaderLifeCycle.getBootstrapClassLoader(), plugins); - - } catch (IOException ex) { - throw new PluginLoadException("could not load plugins", ex); - } - - Injector bootstrapInjector = createBootstrapInjector(pluginLoader); - - startEitherMigrationOrNormalServlet(classLoaderLifeCycle.getBootstrapClassLoader(), plugins, pluginLoader, bootstrapInjector); + startEitherMigrationOrNormalServlet(classLoaderLifeCycle.getBootstrapClassLoader(), pluginBootstrap.getPlugins(), pluginBootstrap.getPluginLoader(), bootstrapInjector); } private void startEitherMigrationOrNormalServlet(ClassLoader cl, Set plugins, PluginLoader pluginLoader, Injector bootstrapInjector) { @@ -185,17 +113,6 @@ public class BootstrapContextListener implements ServletContextListener { } } - private void renameOldPluginsFolder(File pluginDirectory) { - if (new File(pluginDirectory, "classpath.xml").exists()) { - File backupDirectory = new File(pluginDirectory.getParentFile(), "plugins.v1"); - boolean renamed = pluginDirectory.renameTo(backupDirectory); - if (renamed) { - logger.warn("moved old plugins directory to {}", backupDirectory); - } else { - throw new UpdateException("could not rename existing v1 plugin directory"); - } - } - } private MigrationWizardContextListener prepareWizardIfNeeded(Injector bootstrapInjector) { return new MigrationWizardContextListener(bootstrapInjector); @@ -224,213 +141,6 @@ public class BootstrapContextListener implements ServletContextListener { updateEngine.update(); } - private boolean isCorePluginExtractionDisabled() { - return Boolean.getBoolean("sonia.scm.boot.disable-core-plugin-extraction"); - } - - /** - * Method description - * - * - * @param context - * @param pluginDirectory - * @param entry - * - * @throws IOException - */ - private void extractCorePlugin(ServletContext context, File pluginDirectory, - PluginIndexEntry entry) - throws IOException { - URL url = context.getResource(PLUGIN_DIRECTORY.concat(entry.getName())); - SmpArchive archive = SmpArchive.create(url); - Plugin plugin = archive.getPlugin(); - - File directory = PluginsInternal.createPluginDirectory(pluginDirectory, - plugin); - File checksumFile = PluginsInternal.getChecksumFile(directory); - - if (!directory.exists()) { - logger.warn("install plugin {}", plugin.getInformation().getId()); - PluginsInternal.extract(archive, entry.getChecksum(), directory, - checksumFile, true); - } else if (!checksumFile.exists()) { - logger.warn("plugin directory {} exists without checksum file.", - directory); - PluginsInternal.extract(archive, entry.getChecksum(), directory, - checksumFile, true); - } else { - String checksum = Files.toString(checksumFile, Charsets.UTF_8).trim(); - - if (checksum.equals(entry.getChecksum())) { - logger.debug("plugin {} is up to date", - plugin.getInformation().getId()); - } else { - logger.warn("checksum mismatch of pluing {}, start update", - plugin.getInformation().getId()); - PluginsInternal.extract(archive, entry.getChecksum(), directory, - checksumFile, true); - } - } - } - - /** - * Method description - * - * - * @param context - * @param pluginDirectory - * - * @throws IOException - */ - private void extractCorePlugins(ServletContext context, File pluginDirectory) throws IOException { - IOUtil.mkdirs(pluginDirectory); - - PluginIndex index = readCorePluginIndex(context); - - for (PluginIndexEntry entry : index) { - extractCorePlugin(context, pluginDirectory, entry); - } - } - - /** - * Method description - * - * - * @param context - * - * @return - */ - private PluginIndex readCorePluginIndex(ServletContext context) { - PluginIndex index = null; - - try { - URL indexUrl = context.getResource(PLUGIN_COREINDEX); - - if (indexUrl == null) { - throw new PluginException("no core plugin index found"); - } - - index = JAXB.unmarshal(indexUrl, PluginIndex.class); - } catch (MalformedURLException ex) { - throw new PluginException("could not load core plugin index", ex); - } catch (DataBindingException ex) { - throw new PluginException("could not unmarshall core plugin index", ex); - } - - return index; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - private File getPluginDirectory() { - File baseDirectory = SCMContext.getContext().getBaseDirectory(); - - return new File(baseDirectory, DIRECTORY_PLUGINS); - } - - //~--- inner classes -------------------------------------------------------- - - /** - * Class description - * - * - * @version Enter version here..., 14/07/09 - * @author Enter your name here... - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlRootElement(name = "plugin-index") - private static class PluginIndex implements Iterable { - - /** - * Method description - * - * - * @return - */ - @Override - public Iterator iterator() { - return getPlugins().iterator(); - } - - //~--- get methods -------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public List getPlugins() { - if (plugins == null) { - plugins = ImmutableList.of(); - } - - return plugins; - } - - //~--- fields ------------------------------------------------------------- - - /** Field description */ - @XmlElement(name = "plugins") - private List plugins; - } - - - /** - * Class description - * - * - * @version Enter version here..., 14/07/09 - * @author Enter your name here... - */ - @XmlRootElement(name = "plugins") - @XmlAccessorType(XmlAccessType.FIELD) - private static class PluginIndexEntry { - - /** - * Method description - * - * - * @return - */ - public String getChecksum() { - return checksum; - } - - /** - * Method description - * - * - * @return - */ - public String getName() { - return name; - } - - //~--- fields ------------------------------------------------------------- - - /** Field description */ - private String checksum; - - /** Field description */ - private String name; - } - - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private ServletContext context; - - /** Field description */ - private ServletContextListener contextListener; - private static class ScmContextListenerModule extends AbstractModule { @Override protected void configure() { diff --git a/scm-webapp/src/main/java/sonia/scm/boot/PluginBootstrap.java b/scm-webapp/src/main/java/sonia/scm/boot/PluginBootstrap.java new file mode 100644 index 0000000000..c84e69beb9 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/boot/PluginBootstrap.java @@ -0,0 +1,208 @@ +package sonia.scm.boot; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.google.common.io.Files; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.SCMContext; +import sonia.scm.migration.UpdateException; +import sonia.scm.plugin.DefaultPluginLoader; +import sonia.scm.plugin.Plugin; +import sonia.scm.plugin.PluginException; +import sonia.scm.plugin.PluginLoadException; +import sonia.scm.plugin.PluginLoader; +import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.PluginsInternal; +import sonia.scm.plugin.SmpArchive; +import sonia.scm.util.IOUtil; + +import javax.servlet.ServletContext; +import javax.xml.bind.DataBindingException; +import javax.xml.bind.JAXB; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public final class PluginBootstrap { + + private static final Logger LOG = LoggerFactory.getLogger(PluginBootstrap.class); + + private static final String DIRECTORY_PLUGINS = "plugins"; + private static final String PLUGIN_DIRECTORY = "/WEB-INF/plugins/"; + private static final String PLUGIN_COREINDEX = PLUGIN_DIRECTORY.concat("plugin-index.xml"); + + private final ClassLoaderLifeCycle classLoaderLifeCycle; + private final ServletContext servletContext; + private final Set plugins; + private final PluginLoader pluginLoader; + + PluginBootstrap(ServletContext servletContext, ClassLoaderLifeCycle classLoaderLifeCycle) { + this.servletContext = servletContext; + this.classLoaderLifeCycle = classLoaderLifeCycle; + + this.plugins = collectPlugins(); + this.pluginLoader = createPluginLoader(); + } + + public PluginLoader getPluginLoader() { + return pluginLoader; + } + + public Set getPlugins() { + return plugins; + } + + private PluginLoader createPluginLoader() { + return new DefaultPluginLoader(servletContext, classLoaderLifeCycle.getBootstrapClassLoader(), plugins); + } + + private Set collectPlugins() { + try { + File pluginDirectory = getPluginDirectory(); + + renameOldPluginsFolder(pluginDirectory); + + if (!isCorePluginExtractionDisabled()) { + extractCorePlugins(servletContext, pluginDirectory); + } else { + LOG.info("core plugin extraction is disabled"); + } + + return PluginsInternal.collectPlugins(classLoaderLifeCycle, pluginDirectory.toPath()); + } catch (IOException ex) { + throw new PluginLoadException("could not load plugins", ex); + } + } + + private void renameOldPluginsFolder(File pluginDirectory) { + if (new File(pluginDirectory, "classpath.xml").exists()) { + File backupDirectory = new File(pluginDirectory.getParentFile(), "plugins.v1"); + boolean renamed = pluginDirectory.renameTo(backupDirectory); + if (renamed) { + LOG.warn("moved old plugins directory to {}", backupDirectory); + } else { + throw new UpdateException("could not rename existing v1 plugin directory"); + } + } + } + + + private boolean isCorePluginExtractionDisabled() { + return Boolean.getBoolean("sonia.scm.boot.disable-core-plugin-extraction"); + } + + private void extractCorePlugin(ServletContext context, File pluginDirectory, + PluginIndexEntry entry) throws IOException { + URL url = context.getResource(PLUGIN_DIRECTORY.concat(entry.getName())); + SmpArchive archive = SmpArchive.create(url); + Plugin plugin = archive.getPlugin(); + + File directory = PluginsInternal.createPluginDirectory(pluginDirectory, plugin); + File checksumFile = PluginsInternal.getChecksumFile(directory); + + if (!directory.exists()) { + LOG.warn("install plugin {}", plugin.getInformation().getId()); + PluginsInternal.extract(archive, entry.getChecksum(), directory, checksumFile, true); + } else if (!checksumFile.exists()) { + LOG.warn("plugin directory {} exists without checksum file.", directory); + PluginsInternal.extract(archive, entry.getChecksum(), directory, checksumFile, true); + } else { + String checksum = Files.toString(checksumFile, Charsets.UTF_8).trim(); + + if (checksum.equals(entry.getChecksum())) { + LOG.debug("plugin {} is up to date", plugin.getInformation().getId()); + } else { + LOG.warn("checksum mismatch of pluing {}, start update", plugin.getInformation().getId()); + PluginsInternal.extract(archive, entry.getChecksum(), directory, checksumFile, true); + } + } + } + + private void extractCorePlugins(ServletContext context, File pluginDirectory) throws IOException { + IOUtil.mkdirs(pluginDirectory); + + PluginIndex index = readCorePluginIndex(context); + + for (PluginIndexEntry entry : index) { + extractCorePlugin(context, pluginDirectory, entry); + } + } + + + private PluginIndex readCorePluginIndex(ServletContext context) { + PluginIndex index; + + try { + URL indexUrl = context.getResource(PLUGIN_COREINDEX); + + if (indexUrl == null) { + throw new PluginException("no core plugin index found"); + } + + index = JAXB.unmarshal(indexUrl, PluginIndex.class); + } catch (MalformedURLException ex) { + throw new PluginException("could not load core plugin index", ex); + } catch (DataBindingException ex) { + throw new PluginException("could not unmarshal core plugin index", ex); + } + + return index; + } + + private File getPluginDirectory() { + File baseDirectory = SCMContext.getContext().getBaseDirectory(); + + return new File(baseDirectory, DIRECTORY_PLUGINS); + } + + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlRootElement(name = "plugin-index") + private static class PluginIndex implements Iterable { + + @XmlElement(name = "plugins") + private List plugins; + + @Override + public Iterator iterator() { + return getPlugins().iterator(); + } + + public List getPlugins() { + if (plugins == null) { + plugins = ImmutableList.of(); + } + + return plugins; + } + + } + + @XmlRootElement(name = "plugins") + @XmlAccessorType(XmlAccessType.FIELD) + private static class PluginIndexEntry { + + private String checksum; + + private String name; + + public String getName() { + return name; + } + + public String getChecksum() { + return checksum; + } + + } + +}