From 283422d4e39d65645bf109cdcc8d7817d2cb2766 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 12 Jul 2014 17:17:24 +0200 Subject: [PATCH] improve plugin installation and backup installed archives --- .../java/sonia/scm/plugin/SmpArchive.java | 14 +++ .../scm/boot/BootstrapContextListener.java | 49 ++------ .../sonia/scm/plugin/PluginProcessor.java | 116 +++++++++++++++--- .../main/java/sonia/scm/plugin/Plugins.java | 77 +++++++++++- 4 files changed, 193 insertions(+), 63 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java b/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java index 28beb1128e..4c57c57385 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java +++ b/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java @@ -58,6 +58,7 @@ import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; +import java.nio.file.Path; import java.util.Collection; import java.util.zip.ZipEntry; @@ -128,6 +129,19 @@ public final class SmpArchive return create(Resources.asByteSource(archive)); } + /** + * Method description + * + * + * @param archive + * + * @return + */ + public static SmpArchive create(Path archive) + { + return create(archive.toFile()); + } + /** * Method description * 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 b7551ac860..7a99693760 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java @@ -85,9 +85,6 @@ 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/"; @@ -168,41 +165,6 @@ public class BootstrapContextListener implements ServletContextListener contextListener.contextInitialized(sce); } - /** - * Method description - * - * - * @param archive - * @param checksum - * @param directory - * @param checksumFile - * - * @throws IOException - */ - private void extract(SmpArchive archive, String checksum, File directory, - File checksumFile) - throws IOException - { - if (directory.exists()) - { - 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 * @@ -223,18 +185,20 @@ public class BootstrapContextListener implements ServletContextListener PluginId id = archive.getPluginId(); File directory = Plugins.createPluginDirectory(pluginDirectory, id); - File checksumFile = new File(directory, FILE_CHECKSUM); + File checksumFile = Plugins.getChecksumFile(directory); if (!directory.exists()) { logger.warn("install plugin {}", id); - extract(archive, entry.getChecksum(), directory, checksumFile); + Plugins.extract(archive, entry.getChecksum(), directory, checksumFile, + true); } else if (!checksumFile.exists()) { logger.warn("plugin directory {} exists without checksum file.", directory); - extract(archive, entry.getChecksum(), directory, checksumFile); + Plugins.extract(archive, entry.getChecksum(), directory, checksumFile, + true); } else { @@ -247,7 +211,8 @@ public class BootstrapContextListener implements ServletContextListener else { logger.warn("checksum mismatch of pluing {}, start update", id); - extract(archive, entry.getChecksum(), directory, checksumFile); + Plugins.extract(archive, entry.getChecksum(), directory, checksumFile, + true); } } } 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 c746437aa0..d34d28358d 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -36,15 +36,14 @@ package sonia.scm.plugin; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.Sets; +import com.google.common.hash.Hashing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.io.ZipUnArchiver; -import sonia.scm.util.IOUtil; - //~--- JDK imports ------------------------------------------------------------ +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -55,7 +54,10 @@ import java.nio.file.DirectoryStream.Filter; import java.nio.file.Files; import java.nio.file.Path; +import java.text.SimpleDateFormat; + import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Set; @@ -69,15 +71,15 @@ import javax.xml.bind.JAXBException; public final class PluginProcessor { - /** Field description */ - private static final String DESCRIPTOR = "META-INF/scm/plugin.xml"; - /** Field description */ private static final String DIRECTORY_CLASSES = "classes"; /** Field description */ private static final String DIRECTORY_DEPENDENCIES = "lib"; + /** Field description */ + private static final String DIRECTORY_INSTALLED = ".installed"; + /** Field description */ private static final String DIRECTORY_LINK = ".link"; @@ -90,6 +92,10 @@ public final class PluginProcessor /** Field description */ private static final String EXTENSION_PLUGIN = ".smp"; + /** Field description */ + private static final String FILE_DESCRIPTOR = + SmpArchive.PATH_DESCRIPTOR.substring(1); + /** Field description */ private static final String GLOB_JAR = "*.jar"; @@ -110,6 +116,7 @@ public final class PluginProcessor public PluginProcessor(Path pluginDirectory) { this.pluginDirectory = pluginDirectory; + this.installedDirectory = findInstalledDirectory(); try { @@ -186,8 +193,6 @@ public final class PluginProcessor } } - System.out.println(urls); - //J- return new DefaultPluginClassLoader( urls.toArray(new URL[urls.size()]), @@ -309,6 +314,17 @@ public final class PluginProcessor return paths.build(); } + /** + * Method description + * + * + * @return + */ + private String createDate() + { + return new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + } + /** * Method description * @@ -357,7 +373,7 @@ public final class PluginProcessor throws IOException { PluginWrapper wrapper = null; - Path descriptor = directory.resolve(DESCRIPTOR); + Path descriptor = directory.resolve(FILE_DESCRIPTOR); if (Files.exists(descriptor)) { @@ -367,6 +383,10 @@ public final class PluginProcessor wrapper = new PluginWrapper(plugin, cl, directory); } + else + { + logger.warn("found plugin directory without plugin descriptor"); + } return wrapper; } @@ -412,24 +432,81 @@ 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) { + File archiveFile = archive.toFile(); + logger.trace("extract archive {}", archive); - String filename = archive.getFileName().toString(); - Path directory = pluginDirectory.resolve(filename.substring(0, - filename.lastIndexOf('.'))); + SmpArchive smp = SmpArchive.create(archive); - IOUtil.extract(archive.toFile(), directory.toFile(), - ZipUnArchiver.EXTENSION); - Files.delete(archive); + logger.debug("extract plugin {}", smp.getPluginId()); + + File directory = Plugins.createPluginDirectory(archiveFile, + smp.getPluginId()); + + String checksum = com.google.common.io.Files.hash(archiveFile, + Hashing.sha256()).toString(); + File checksumFile = Plugins.getChecksumFile(directory); + + Plugins.extract(smp, checksum, directory, checksumFile, false); + moveArchive(archive); } } + /** + * Method description + * + * + * @return + */ + private Path findInstalledDirectory() + { + Path directory = null; + Path installed = pluginDirectory.resolve(DIRECTORY_INSTALLED); + Path date = installed.resolve(createDate()); + + for (int i = 0; i < 999; i++) + { + Path dir = date.resolve(String.format("%03d", i)); + + if (!Files.exists(dir)) + { + directory = dir; + + break; + } + } + + if (directory == null) + { + throw new PluginException("could not find installed directory"); + } + + return directory; + } + + /** + * Method description + * + * + * @param archive + * + * @throws IOException + */ + private void moveArchive(Path archive) throws IOException + { + if (!Files.exists(installedDirectory)) + { + logger.debug("create installed directory {}", installedDirectory); + Files.createDirectories(installedDirectory); + } + + Files.move(archive, installedDirectory.resolve(archive.getFileName())); + } + //~--- inner classes -------------------------------------------------------- /** @@ -455,7 +532,7 @@ public final class PluginProcessor @Override public boolean accept(Path entry) throws IOException { - return Files.isDirectory(entry); + return Files.isDirectory(entry) &&!entry.getFileName().startsWith("."); } } @@ -495,6 +572,9 @@ public final class PluginProcessor /** Field description */ private final JAXBContext context; + /** Field description */ + private final Path installedDirectory; + /** Field description */ private final Path pluginDirectory; } 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 50eb04cdce..38209ed9c0 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/Plugins.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/Plugins.java @@ -10,12 +10,19 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.collect.Iterables; -import java.io.File; +import com.google.common.io.Files; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.util.IOUtil; //~--- JDK imports ------------------------------------------------------------ +import java.io.File; import java.io.IOException; import java.nio.file.Path; @@ -29,6 +36,13 @@ import java.util.Set; public final class Plugins { + /** + * the logger for Plugins + */ + private static final Logger logger = LoggerFactory.getLogger(Plugins.class); + + //~--- constructors --------------------------------------------------------- + /** * Constructs ... * @@ -56,11 +70,68 @@ public final class Plugins return processor.collectPlugins(classLoader); } - - public static File createPluginDirectory(File parent, PluginId id){ + + /** + * Method description + * + * + * @param parent + * @param id + * + * @return + */ + public static File createPluginDirectory(File parent, PluginId id) + { return new File(new File(parent, id.getGroupId()), id.getArtifactId()); } + /** Field description */ + private static final String FILE_CHECKSUM = "checksum"; + + + + /** + * Method description + * + * + * @param archive + * @param checksum + * @param directory + * @param checksumFile + * @param core + * + * @throws IOException + */ + public static void extract(SmpArchive archive, String checksum, + File directory, File checksumFile, boolean core) + throws IOException + { + if (directory.exists()) + { + logger.debug("delete directory {} for plugin extraction", + archive.getPluginId()); + IOUtil.delete(directory); + } + + IOUtil.mkdirs(directory); + + logger.debug("extract plugin {}", archive.getPluginId()); + archive.extract(directory); + Files.write(checksum, checksumFile, Charsets.UTF_8); + + if (core) + { + if (!new File(directory, "core").createNewFile()) + { + throw new IOException("could not create core plugin marker"); + } + } + } + + public static File getChecksumFile(File pluginDirectory){ + return new File(pluginDirectory, FILE_CHECKSUM); + } + /** * Method description *