From 598f8c95baf0b57755f24de63af6d64f77b53ae6 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 12 Jul 2014 16:41:20 +0200 Subject: [PATCH] use new core plugin index and improved plugin directory structure --- .../scm/boot/BootstrapContextListener.java | 253 +++++++++++++++--- .../sonia/scm/plugin/PluginProcessor.java | 37 ++- .../main/java/sonia/scm/plugin/Plugins.java | 5 + 3 files changed, 249 insertions(+), 46 deletions(-) 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 8a11d2a43c..b7551ac860 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java @@ -34,8 +34,8 @@ package sonia.scm.boot; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Charsets; -import com.google.common.base.Strings; -import com.google.common.io.Resources; +import com.google.common.collect.ImmutableList; +import com.google.common.io.Files; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,9 +43,11 @@ import org.slf4j.LoggerFactory; import sonia.scm.SCMContext; import sonia.scm.ScmContextListener; import sonia.scm.plugin.PluginException; +import sonia.scm.plugin.PluginId; import sonia.scm.plugin.PluginLoadException; import sonia.scm.plugin.PluginWrapper; import sonia.scm.plugin.Plugins; +import sonia.scm.plugin.SmpArchive; import sonia.scm.util.ClassLoaders; import sonia.scm.util.IOUtil; @@ -53,12 +55,12 @@ import sonia.scm.util.IOUtil; import java.io.Closeable; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; +import java.net.MalformedURLException; import java.net.URL; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -66,6 +68,13 @@ import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; +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; + /** * * @author Sebastian Sdorra @@ -76,6 +85,9 @@ public class BootstrapContextListener implements ServletContextListener /** Field description */ private static final String DIRECTORY_PLUGINS = "plugins"; + /** Field description */ + private static final String FILE_CHECKSUM = "checksum"; + /** Field description */ private static final String PLUGIN_DIRECTORY = "/WEB-INF/plugins/"; @@ -87,7 +99,7 @@ public class BootstrapContextListener implements ServletContextListener /** Field description */ private static final String PLUGIN_COREINDEX = - PLUGIN_DIRECTORY.concat("plugin.idx"); + PLUGIN_DIRECTORY.concat("plugin-index.xml"); //~--- methods -------------------------------------------------------------- @@ -132,17 +144,17 @@ public class BootstrapContextListener implements ServletContextListener public void contextInitialized(ServletContextEvent sce) { ServletContext context = sce.getServletContext(); - List lines = readCorePluginIndex(context); + PluginIndex index = readCorePluginIndex(context); File pluginDirectory = getPluginDirectory(); - copyCorePlugins(context, pluginDirectory, lines); - - ClassLoader cl = - ClassLoaders.getContextClassLoader(BootstrapContextListener.class); - try { + extractCorePlugins(context, pluginDirectory, index); + + ClassLoader cl = + ClassLoaders.getContextClassLoader(BootstrapContextListener.class); + Set plugins = Plugins.collectPlugins(cl, pluginDirectory.toPath()); @@ -160,22 +172,83 @@ public class BootstrapContextListener implements ServletContextListener * Method description * * - * @param context - * @param pluginDirectory - * @param name + * @param archive + * @param checksum + * @param directory + * @param checksumFile * * @throws IOException */ - private void copyCorePlugin(ServletContext context, File pluginDirectory, - String name) + private void extract(SmpArchive archive, String checksum, File directory, + File checksumFile) throws IOException { - URL url = context.getResource(PLUGIN_DIRECTORY.concat(name)); - File file = new File(pluginDirectory, name); - - try (OutputStream output = new FileOutputStream(file)) + if (directory.exists()) { - Resources.copy(url, output); + logger.debug("delete directory {} for plugin extraction", + archive.getPluginId()); + IOUtil.delete(directory); + } + + IOUtil.mkdirs(directory); + + logger.debug("extract plugin {}", archive.getPluginId()); + archive.extract(directory); + //J- + com.google.common.io.Files.write( + checksum, + checksumFile, + Charsets.UTF_8 + ); + //J+ + } + + /** + * Method description + * + * + * @param context + * @param pluginDirectory + * @param name + * @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); + PluginId id = archive.getPluginId(); + + File directory = Plugins.createPluginDirectory(pluginDirectory, id); + File checksumFile = new File(directory, FILE_CHECKSUM); + + if (!directory.exists()) + { + logger.warn("install plugin {}", id); + extract(archive, entry.getChecksum(), directory, checksumFile); + } + else if (!checksumFile.exists()) + { + logger.warn("plugin directory {} exists without checksum file.", + directory); + extract(archive, entry.getChecksum(), directory, checksumFile); + } + else + { + String checksum = Files.toString(checksumFile, Charsets.UTF_8).trim(); + + if (checksum.equals(entry.getChecksum())) + { + logger.debug("plugin {} is up to date", id); + } + else + { + logger.warn("checksum mismatch of pluing {}, start update", id); + extract(archive, entry.getChecksum(), directory, checksumFile); + } } } @@ -186,27 +259,19 @@ public class BootstrapContextListener implements ServletContextListener * @param context * @param pluginDirectory * @param lines + * @param index + * + * @throws IOException */ - private void copyCorePlugins(ServletContext context, File pluginDirectory, - List lines) + private void extractCorePlugins(ServletContext context, File pluginDirectory, + PluginIndex index) + throws IOException { IOUtil.mkdirs(pluginDirectory); - for (String line : lines) + for (PluginIndexEntry entry : index) { - line = line.trim(); - - if (!Strings.isNullOrEmpty(line)) - { - try - { - copyCorePlugin(context, pluginDirectory, line); - } - catch (IOException ex) - { - logger.error("could not copy core plugin", ex); - } - } + extractCorePlugin(context, pluginDirectory, entry); } } @@ -218,27 +283,31 @@ public class BootstrapContextListener implements ServletContextListener * * @return */ - private List readCorePluginIndex(ServletContext context) + private PluginIndex readCorePluginIndex(ServletContext context) { - List lines; + PluginIndex index = null; try { - URL index = context.getResource(PLUGIN_COREINDEX); + URL indexUrl = context.getResource(PLUGIN_COREINDEX); - if (index == null) + if (indexUrl == null) { throw new PluginException("no core plugin index found"); } - lines = Resources.readLines(index, Charsets.UTF_8); + index = JAXB.unmarshal(indexUrl, PluginIndex.class); } - catch (IOException ex) + 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 lines; + return index; } //~--- get methods ---------------------------------------------------------- @@ -256,6 +325,102 @@ public class BootstrapContextListener implements ServletContextListener 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 */ diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java index 52fb75b5d7..c746437aa0 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -34,6 +34,7 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.Sets; import org.slf4j.Logger; @@ -184,7 +185,7 @@ public final class PluginProcessor } } } - + System.out.println(urls); //J- @@ -236,7 +237,7 @@ public final class PluginProcessor extract(archives); - Set directories = collect(pluginDirectory, new DirectoryFilter()); + Set directories = collectPluginDirectories(pluginDirectory); if (logger.isDebugEnabled()) { @@ -278,6 +279,36 @@ public final class PluginProcessor return paths; } + /** + * Method description + * + * + * @param directory + * + * @return + * + * @throws IOException + */ + private Set collectPluginDirectories(Path directory) throws IOException + { + Builder paths = ImmutableSet.builder(); + + Filter filter = new DirectoryFilter(); + + try (DirectoryStream parentStream = stream(directory, filter)) + { + for (Path parent : parentStream) + { + try (DirectoryStream direcotries = stream(parent, filter)) + { + paths.addAll(direcotries); + } + } + } + + return paths.build(); + } + /** * Method description * @@ -381,6 +412,8 @@ public final class PluginProcessor */ private void extract(Iterable archives) throws IOException { + + // TODO use SmpArchive and new path logger.debug("extract archives"); for (Path archive : archives) diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/Plugins.java b/scm-webapp/src/main/java/sonia/scm/plugin/Plugins.java index 6a08755b36..50eb04cdce 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/Plugins.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/Plugins.java @@ -12,6 +12,7 @@ package sonia.scm.plugin; import com.google.common.base.Function; import com.google.common.collect.Iterables; +import java.io.File; //~--- JDK imports ------------------------------------------------------------ @@ -55,6 +56,10 @@ public final class Plugins return processor.collectPlugins(classLoader); } + + public static File createPluginDirectory(File parent, PluginId id){ + return new File(new File(parent, id.getGroupId()), id.getArtifactId()); + } /** * Method description