mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 17:05:43 +01:00
move bootstrapping of plugins into own class PluginBootstrap
This commit is contained in:
@@ -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<PluginWrapper> 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<PluginWrapper> 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<PluginIndexEntry> {
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Iterator<PluginIndexEntry> iterator() {
|
||||
return getPlugins().iterator();
|
||||
}
|
||||
|
||||
//~--- get methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<PluginIndexEntry> getPlugins() {
|
||||
if (plugins == null) {
|
||||
plugins = ImmutableList.of();
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "plugins")
|
||||
private List<PluginIndexEntry> 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() {
|
||||
|
||||
208
scm-webapp/src/main/java/sonia/scm/boot/PluginBootstrap.java
Normal file
208
scm-webapp/src/main/java/sonia/scm/boot/PluginBootstrap.java
Normal file
@@ -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<PluginWrapper> 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<PluginWrapper> getPlugins() {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
private PluginLoader createPluginLoader() {
|
||||
return new DefaultPluginLoader(servletContext, classLoaderLifeCycle.getBootstrapClassLoader(), plugins);
|
||||
}
|
||||
|
||||
private Set<PluginWrapper> 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<PluginIndexEntry> {
|
||||
|
||||
@XmlElement(name = "plugins")
|
||||
private List<PluginIndexEntry> plugins;
|
||||
|
||||
@Override
|
||||
public Iterator<PluginIndexEntry> iterator() {
|
||||
return getPlugins().iterator();
|
||||
}
|
||||
|
||||
public List<PluginIndexEntry> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user