mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
use new core plugin index and improved plugin directory structure
This commit is contained in:
@@ -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<String> lines = readCorePluginIndex(context);
|
||||
PluginIndex index = readCorePluginIndex(context);
|
||||
|
||||
File pluginDirectory = getPluginDirectory();
|
||||
|
||||
copyCorePlugins(context, pluginDirectory, lines);
|
||||
try
|
||||
{
|
||||
extractCorePlugins(context, pluginDirectory, index);
|
||||
|
||||
ClassLoader cl =
|
||||
ClassLoaders.getContextClassLoader(BootstrapContextListener.class);
|
||||
|
||||
try
|
||||
{
|
||||
Set<PluginWrapper> 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<String> 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<String> readCorePluginIndex(ServletContext context)
|
||||
private PluginIndex readCorePluginIndex(ServletContext context)
|
||||
{
|
||||
List<String> 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<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 */
|
||||
|
||||
@@ -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;
|
||||
@@ -236,7 +237,7 @@ public final class PluginProcessor
|
||||
|
||||
extract(archives);
|
||||
|
||||
Set<Path> directories = collect(pluginDirectory, new DirectoryFilter());
|
||||
Set<Path> 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<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
|
||||
*
|
||||
@@ -381,6 +412,8 @@ public final class PluginProcessor
|
||||
*/
|
||||
private void extract(Iterable<Path> archives) throws IOException
|
||||
{
|
||||
|
||||
// TODO use SmpArchive and new path
|
||||
logger.debug("extract archives");
|
||||
|
||||
for (Path archive : archives)
|
||||
|
||||
@@ -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 ------------------------------------------------------------
|
||||
|
||||
@@ -56,6 +57,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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user