mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 14:35: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 --------------------------------------------------------
|
//~--- 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 */
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user