use new core plugin index and improved plugin directory structure

This commit is contained in:
Sebastian Sdorra
2014-07-12 16:41:20 +02:00
parent 80e52a347a
commit 598f8c95ba
3 changed files with 249 additions and 46 deletions

View File

@@ -34,8 +34,8 @@ package sonia.scm.boot;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.base.Strings; import com.google.common.collect.ImmutableList;
import com.google.common.io.Resources; import com.google.common.io.Files;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -43,9 +43,11 @@ import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext; import sonia.scm.SCMContext;
import sonia.scm.ScmContextListener; import sonia.scm.ScmContextListener;
import sonia.scm.plugin.PluginException; import sonia.scm.plugin.PluginException;
import sonia.scm.plugin.PluginId;
import sonia.scm.plugin.PluginLoadException; import sonia.scm.plugin.PluginLoadException;
import sonia.scm.plugin.PluginWrapper; import sonia.scm.plugin.PluginWrapper;
import sonia.scm.plugin.Plugins; import sonia.scm.plugin.Plugins;
import sonia.scm.plugin.SmpArchive;
import sonia.scm.util.ClassLoaders; import sonia.scm.util.ClassLoaders;
import sonia.scm.util.IOUtil; import sonia.scm.util.IOUtil;
@@ -53,12 +55,12 @@ import sonia.scm.util.IOUtil;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -66,6 +68,13 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; 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 * @author Sebastian Sdorra
@@ -76,6 +85,9 @@ public class BootstrapContextListener implements ServletContextListener
/** Field description */ /** Field description */
private static final String DIRECTORY_PLUGINS = "plugins"; private static final String DIRECTORY_PLUGINS = "plugins";
/** Field description */
private static final String FILE_CHECKSUM = "checksum";
/** Field description */ /** Field description */
private static final String PLUGIN_DIRECTORY = "/WEB-INF/plugins/"; private static final String PLUGIN_DIRECTORY = "/WEB-INF/plugins/";
@@ -87,7 +99,7 @@ public class BootstrapContextListener implements ServletContextListener
/** Field description */ /** Field description */
private static final String PLUGIN_COREINDEX = private static final String PLUGIN_COREINDEX =
PLUGIN_DIRECTORY.concat("plugin.idx"); PLUGIN_DIRECTORY.concat("plugin-index.xml");
//~--- methods -------------------------------------------------------------- //~--- methods --------------------------------------------------------------
@@ -132,17 +144,17 @@ public class BootstrapContextListener implements ServletContextListener
public void contextInitialized(ServletContextEvent sce) public void contextInitialized(ServletContextEvent sce)
{ {
ServletContext context = sce.getServletContext(); ServletContext context = sce.getServletContext();
List<String> lines = readCorePluginIndex(context); PluginIndex index = readCorePluginIndex(context);
File pluginDirectory = getPluginDirectory(); File pluginDirectory = getPluginDirectory();
copyCorePlugins(context, pluginDirectory, lines); try
{
extractCorePlugins(context, pluginDirectory, index);
ClassLoader cl = ClassLoader cl =
ClassLoaders.getContextClassLoader(BootstrapContextListener.class); ClassLoaders.getContextClassLoader(BootstrapContextListener.class);
try
{
Set<PluginWrapper> plugins = Plugins.collectPlugins(cl, Set<PluginWrapper> plugins = Plugins.collectPlugins(cl,
pluginDirectory.toPath()); pluginDirectory.toPath());
@@ -160,22 +172,83 @@ public class BootstrapContextListener implements ServletContextListener
* Method description * Method description
* *
* *
* @param context * @param archive
* @param pluginDirectory * @param checksum
* @param name * @param directory
* @param checksumFile
* *
* @throws IOException * @throws IOException
*/ */
private void copyCorePlugin(ServletContext context, File pluginDirectory, private void extract(SmpArchive archive, String checksum, File directory,
String name) File checksumFile)
throws IOException throws IOException
{ {
URL url = context.getResource(PLUGIN_DIRECTORY.concat(name)); if (directory.exists())
File file = new File(pluginDirectory, name);
try (OutputStream output = new FileOutputStream(file))
{ {
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 context
* @param pluginDirectory * @param pluginDirectory
* @param lines * @param lines
* @param index
*
* @throws IOException
*/ */
private void copyCorePlugins(ServletContext context, File pluginDirectory, private void extractCorePlugins(ServletContext context, File pluginDirectory,
List<String> lines) PluginIndex index)
throws IOException
{ {
IOUtil.mkdirs(pluginDirectory); IOUtil.mkdirs(pluginDirectory);
for (String line : lines) for (PluginIndexEntry entry : index)
{ {
line = line.trim(); extractCorePlugin(context, pluginDirectory, entry);
if (!Strings.isNullOrEmpty(line))
{
try
{
copyCorePlugin(context, pluginDirectory, line);
}
catch (IOException ex)
{
logger.error("could not copy core plugin", ex);
}
}
} }
} }
@@ -218,27 +283,31 @@ public class BootstrapContextListener implements ServletContextListener
* *
* @return * @return
*/ */
private List<String> readCorePluginIndex(ServletContext context) private PluginIndex readCorePluginIndex(ServletContext context)
{ {
List<String> lines; PluginIndex index = null;
try 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"); 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); 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 ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
@@ -256,6 +325,102 @@ public class BootstrapContextListener implements ServletContextListener
return new File(baseDirectory, DIRECTORY_PLUGINS); 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 --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */ /** Field description */

View File

@@ -34,6 +34,7 @@ package sonia.scm.plugin;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -236,7 +237,7 @@ public final class PluginProcessor
extract(archives); extract(archives);
Set<Path> directories = collect(pluginDirectory, new DirectoryFilter()); Set<Path> directories = collectPluginDirectories(pluginDirectory);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
@@ -278,6 +279,36 @@ public final class PluginProcessor
return paths; return paths;
} }
/**
* Method description
*
*
* @param directory
*
* @return
*
* @throws IOException
*/
private Set<Path> collectPluginDirectories(Path directory) throws IOException
{
Builder<Path> paths = ImmutableSet.builder();
Filter<Path> filter = new DirectoryFilter();
try (DirectoryStream<Path> parentStream = stream(directory, filter))
{
for (Path parent : parentStream)
{
try (DirectoryStream<Path> direcotries = stream(parent, filter))
{
paths.addAll(direcotries);
}
}
}
return paths.build();
}
/** /**
* Method description * Method description
* *
@@ -381,6 +412,8 @@ public final class PluginProcessor
*/ */
private void extract(Iterable<Path> archives) throws IOException private void extract(Iterable<Path> archives) throws IOException
{ {
// TODO use SmpArchive and new path
logger.debug("extract archives"); logger.debug("extract archives");
for (Path archive : archives) for (Path archive : archives)

View File

@@ -12,6 +12,7 @@ package sonia.scm.plugin;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import java.io.File;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
@@ -56,6 +57,10 @@ public final class Plugins
return processor.collectPlugins(classLoader); return processor.collectPlugins(classLoader);
} }
public static File createPluginDirectory(File parent, PluginId id){
return new File(new File(parent, id.getGroupId()), id.getArtifactId());
}
/** /**
* Method description * Method description
* *