From 056a81d9f9266b4014c229c47a6b798d9907d447 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 07:53:17 +0200 Subject: [PATCH 01/27] fixed dependency mapping --- .../main/java/sonia/scm/plugin/PluginCenterDto.java | 13 +++---------- .../sonia/scm/plugin/PluginCenterDtoMapperTest.java | 7 ++++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java index 8bb48c8ceb..afb8e739a0 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java @@ -11,6 +11,7 @@ import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; import java.util.List; import java.util.Map; +import java.util.Set; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) @@ -56,8 +57,8 @@ public final class PluginCenterDto implements Serializable { @XmlElement(name = "conditions") private Condition conditions; - @XmlElement(name = "dependecies") - private Dependency dependencies; + @XmlElement(name = "dependencies") + private Set dependencies; @XmlElement(name = "_links") private Map links; @@ -74,14 +75,6 @@ public final class PluginCenterDto implements Serializable { private String minVersion; } - @XmlAccessorType(XmlAccessType.FIELD) - @XmlRootElement(name = "dependencies") - @Getter - @AllArgsConstructor - static class Dependency { - private String name; - } - @XmlAccessorType(XmlAccessType.FIELD) @Getter static class Link { diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java index 66a90255b3..831e847843 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java @@ -1,5 +1,6 @@ package sonia.scm.plugin; +import com.google.common.collect.ImmutableSet; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -27,7 +28,7 @@ class PluginCenterDtoMapperTest { "http://avatar.url", "555000444", new Condition(Collections.singletonList("linux"), "amd64","2.0.0"), - new Dependency("scm-review-plugin"), + ImmutableSet.of("scm-review-plugin"), new HashMap<>()); PluginInformation result = PluginCenterDtoMapper.map(Collections.singletonList(plugin)).iterator().next(); @@ -54,7 +55,7 @@ class PluginCenterDtoMapperTest { "https://avatar.url", "12345678aa", new Condition(Collections.singletonList("linux"), "amd64","2.0.0"), - new Dependency("scm-review-plugin"), + ImmutableSet.of("scm-review-plugin"), new HashMap<>()); Plugin plugin2 = new Plugin( @@ -67,7 +68,7 @@ class PluginCenterDtoMapperTest { "http://avatar.url", "555000444", new Condition(Collections.singletonList("linux"), "amd64","2.0.0"), - new Dependency("scm-review-plugin"), + ImmutableSet.of("scm-review-plugin"), new HashMap<>()); Set resultSet = PluginCenterDtoMapper.map(Arrays.asList(plugin1, plugin2)); From ae19ad9327adb0fb4030bcee5bd15bf72214c7f1 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 07:54:00 +0200 Subject: [PATCH 02/27] renamed PluginWrapper to InstalledPlugin --- ...luginWrapper.java => InstalledPlugin.java} | 6 ++-- .../java/sonia/scm/plugin/PluginLoader.java | 2 +- .../v2/resources/InstalledPluginResource.java | 4 +-- .../resources/PluginDtoCollectionMapper.java | 4 +-- .../scm/api/v2/resources/PluginDtoMapper.java | 4 +-- .../UIPluginDtoCollectionMapper.java | 4 +-- .../api/v2/resources/UIPluginDtoMapper.java | 6 ++-- .../api/v2/resources/UIPluginResource.java | 6 ++-- .../sonia/scm/lifecycle/PluginBootstrap.java | 8 ++--- .../sonia/scm/plugin/DefaultPluginLoader.java | 6 ++-- .../scm/plugin/DefaultPluginManager.java | 2 +- .../plugin/DefaultUberWebResourceLoader.java | 10 +++--- .../java/sonia/scm/plugin/PluginNode.java | 6 ++-- .../sonia/scm/plugin/PluginProcessor.java | 26 +++++++-------- .../sonia/scm/plugin/PluginsInternal.java | 10 +++--- .../sonia/scm/plugin/UberClassLoader.java | 10 +++--- .../InstalledPluginResourceTest.java | 14 ++++---- .../api/v2/resources/UIRootResourceTest.java | 8 ++--- .../DefaultUberWebResourceLoaderTest.java | 28 ++++++++-------- .../sonia/scm/plugin/PluginProcessorTest.java | 32 +++++++++---------- 20 files changed, 98 insertions(+), 98 deletions(-) rename scm-core/src/main/java/sonia/scm/plugin/{PluginWrapper.java => InstalledPlugin.java} (95%) diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginWrapper.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java similarity index 95% rename from scm-core/src/main/java/sonia/scm/plugin/PluginWrapper.java rename to scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java index 46c3a4a980..8e93953074 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginWrapper.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java @@ -42,7 +42,7 @@ import java.nio.file.Path; * @author Sebastian Sdorra * @since 2.0.0 */ -public final class PluginWrapper +public final class InstalledPlugin { /** @@ -53,8 +53,8 @@ public final class PluginWrapper * @param webResourceLoader web resource loader * @param directory plugin directory */ - public PluginWrapper(Plugin plugin, ClassLoader classLoader, - WebResourceLoader webResourceLoader, Path directory) + public InstalledPlugin(Plugin plugin, ClassLoader classLoader, + WebResourceLoader webResourceLoader, Path directory) { this.plugin = plugin; this.classLoader = classLoader; diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java index 2d65d1cc98..e82d945024 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java @@ -68,7 +68,7 @@ public interface PluginLoader * * @return */ - public Collection getInstalledPlugins(); + public Collection getInstalledPlugins(); /** * Returns a {@link ClassLoader} which is able to load classes and resources diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java index f10912e5ac..230b34171b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java @@ -7,7 +7,7 @@ import sonia.scm.plugin.Plugin; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -53,7 +53,7 @@ public class InstalledPluginResource { @Produces(VndMediaType.PLUGIN_COLLECTION) public Response getInstalledPlugins() { PluginPermissions.read().check(); - List plugins = new ArrayList<>(pluginLoader.getInstalledPlugins()); + List plugins = new ArrayList<>(pluginLoader.getInstalledPlugins()); return Response.ok(collectionMapper.map(plugins)).build(); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java index 5d8746c211..c835362df7 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java @@ -5,7 +5,7 @@ import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import sonia.scm.plugin.PluginInformation; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import java.util.Collection; import java.util.List; @@ -25,7 +25,7 @@ public class PluginDtoCollectionMapper { this.mapper = mapper; } - public HalRepresentation map(List plugins) { + public HalRepresentation map(List plugins) { List dtos = plugins.stream().map(mapper::map).collect(toList()); return new HalRepresentation(createInstalledPluginsLinks(), embedDtos(dtos)); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index ca81edd7ff..4967c55b31 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -7,7 +7,7 @@ import org.mapstruct.MappingTarget; import org.mapstruct.ObjectFactory; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginState; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import javax.inject.Inject; @@ -20,7 +20,7 @@ public abstract class PluginDtoMapper { @Inject private ResourceLinks resourceLinks; - public PluginDto map(PluginWrapper plugin) { + public PluginDto map(InstalledPlugin plugin) { return map(plugin.getPlugin().getInformation()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoCollectionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoCollectionMapper.java index f032650d8a..2cb15f7904 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoCollectionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoCollectionMapper.java @@ -4,7 +4,7 @@ import com.google.inject.Inject; import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import java.util.Collection; import java.util.List; @@ -24,7 +24,7 @@ public class UIPluginDtoCollectionMapper { this.mapper = mapper; } - public HalRepresentation map(Collection plugins) { + public HalRepresentation map(Collection plugins) { List dtos = plugins.stream().map(mapper::map).collect(toList()); return new HalRepresentation(createLinks(), embedDtos(dtos)); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java index 10ae79b5bf..8a2b6cb0c1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java @@ -2,7 +2,7 @@ package sonia.scm.api.v2.resources; import com.google.common.base.Strings; import de.otto.edison.hal.Links; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.util.HttpUtil; import javax.inject.Inject; @@ -25,7 +25,7 @@ public class UIPluginDtoMapper { this.request = request; } - public UIPluginDto map(PluginWrapper plugin) { + public UIPluginDto map(InstalledPlugin plugin) { UIPluginDto dto = new UIPluginDto( plugin.getPlugin().getInformation().getName(), getScriptResources(plugin) @@ -40,7 +40,7 @@ public class UIPluginDtoMapper { return dto; } - private Set getScriptResources(PluginWrapper wrapper) { + private Set getScriptResources(InstalledPlugin wrapper) { Set scriptResources = wrapper.getPlugin().getResources().getScriptResources(); if (scriptResources != null) { return scriptResources.stream() diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java index b83f5310e3..d34bcbe3ba 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java @@ -4,7 +4,7 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.plugin.PluginLoader; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.security.AllowAnonymousAccess; import sonia.scm.web.VndMediaType; @@ -46,7 +46,7 @@ public class UIPluginResource { @TypeHint(CollectionDto.class) @Produces(VndMediaType.UI_PLUGIN_COLLECTION) public Response getInstalledPlugins() { - List plugins = pluginLoader.getInstalledPlugins() + List plugins = pluginLoader.getInstalledPlugins() .stream() .filter(this::filter) .collect(Collectors.toList()); @@ -85,7 +85,7 @@ public class UIPluginResource { } } - private boolean filter(PluginWrapper plugin) { + private boolean filter(InstalledPlugin plugin) { return plugin.getPlugin().getResources() != null; } diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java index e19d41cc69..3d7e6ec0b2 100644 --- a/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java +++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java @@ -13,7 +13,7 @@ import sonia.scm.plugin.Plugin; import sonia.scm.plugin.PluginException; import sonia.scm.plugin.PluginLoadException; import sonia.scm.plugin.PluginLoader; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.plugin.PluginsInternal; import sonia.scm.plugin.SmpArchive; import sonia.scm.util.IOUtil; @@ -43,7 +43,7 @@ public final class PluginBootstrap { private final ClassLoaderLifeCycle classLoaderLifeCycle; private final ServletContext servletContext; - private final Set plugins; + private final Set plugins; private final PluginLoader pluginLoader; PluginBootstrap(ServletContext servletContext, ClassLoaderLifeCycle classLoaderLifeCycle) { @@ -58,7 +58,7 @@ public final class PluginBootstrap { return pluginLoader; } - public Set getPlugins() { + public Set getPlugins() { return plugins; } @@ -66,7 +66,7 @@ public final class PluginBootstrap { return new DefaultPluginLoader(servletContext, classLoaderLifeCycle.getBootstrapClassLoader(), plugins); } - private Set collectPlugins() { + private Set collectPlugins() { try { File pluginDirectory = getPluginDirectory(); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java index cc3ef01c56..1e4a4b92d7 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java @@ -85,7 +85,7 @@ public class DefaultPluginLoader implements PluginLoader * @param installedPlugins */ public DefaultPluginLoader(ServletContext servletContext, ClassLoader parent, - Set installedPlugins) + Set installedPlugins) { this.installedPlugins = installedPlugins; this.uberClassLoader = new UberClassLoader(parent, installedPlugins); @@ -141,7 +141,7 @@ public class DefaultPluginLoader implements PluginLoader * @return */ @Override - public Collection getInstalledPlugins() + public Collection getInstalledPlugins() { return installedPlugins; } @@ -227,7 +227,7 @@ public class DefaultPluginLoader implements PluginLoader private final ExtensionProcessor extensionProcessor; /** Field description */ - private final Set installedPlugins; + private final Set installedPlugins; /** Field description */ private final Set modules; diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index b718a43a81..70cbab23ff 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -129,7 +129,7 @@ public class DefaultPluginManager implements PluginManager this.httpClient = httpClient; installedPlugins = new HashMap<>(); - for (PluginWrapper wrapper : pluginLoader.getInstalledPlugins()) + for (InstalledPlugin wrapper : pluginLoader.getInstalledPlugins()) { Plugin plugin = wrapper.getPlugin(); PluginInformation info = plugin.getInformation(); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultUberWebResourceLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultUberWebResourceLoader.java index 25b8390e53..c0500245a6 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultUberWebResourceLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultUberWebResourceLoader.java @@ -71,11 +71,11 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader //~--- constructors --------------------------------------------------------- - public DefaultUberWebResourceLoader(ServletContext servletContext, Iterable plugins) { + public DefaultUberWebResourceLoader(ServletContext servletContext, Iterable plugins) { this(servletContext, plugins, SCMContext.getContext().getStage()); } - public DefaultUberWebResourceLoader(ServletContext servletContext, Iterable plugins, Stage stage) { + public DefaultUberWebResourceLoader(ServletContext servletContext, Iterable plugins, Stage stage) { this.servletContext = servletContext; this.plugins = plugins; this.cache = createCache(stage); @@ -153,7 +153,7 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader resources.add(ctxResource); } - for (PluginWrapper wrapper : plugins) + for (InstalledPlugin wrapper : plugins) { URL resource = nonDirectory(wrapper.getWebResourceLoader().getResource(path)); @@ -205,7 +205,7 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader if (resource == null) { - for (PluginWrapper wrapper : plugins) + for (InstalledPlugin wrapper : plugins) { resource = nonDirectory(wrapper.getWebResourceLoader().getResource(path)); @@ -259,7 +259,7 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader private final Cache cache; /** Field description */ - private final Iterable plugins; + private final Iterable plugins; /** Field description */ private final ServletContext servletContext; diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java index 8bc4f47658..94a048770f 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java @@ -157,7 +157,7 @@ public final class PluginNode * * @return */ - public PluginWrapper getWrapper() + public InstalledPlugin getWrapper() { return wrapper; } @@ -170,7 +170,7 @@ public final class PluginNode * * @param wrapper */ - public void setWrapper(PluginWrapper wrapper) + public void setWrapper(InstalledPlugin wrapper) { this.wrapper = wrapper; } @@ -192,5 +192,5 @@ public final class PluginNode private final ExplodedSmp plugin; /** Field description */ - private PluginWrapper wrapper; + private InstalledPlugin wrapper; } 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 b91ee9b1ee..3a168f43d2 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -160,7 +160,7 @@ public final class PluginProcessor * * @throws IOException */ - public Set collectPlugins(ClassLoader classLoader) + public Set collectPlugins(ClassLoader classLoader) throws IOException { logger.info("collect plugins"); @@ -187,7 +187,7 @@ public final class PluginProcessor logger.trace("create plugin wrappers and build classloaders"); - Set wrappers = createPluginWrappers(classLoader, rootNodes); + Set wrappers = createPluginWrappers(classLoader, rootNodes); logger.debug("collected {} plugins", wrappers.size()); @@ -204,7 +204,7 @@ public final class PluginProcessor * * @throws IOException */ - private void appendPluginWrapper(Set plugins, + private void appendPluginWrapper(Set plugins, ClassLoader classLoader, PluginNode node) throws IOException { @@ -217,7 +217,7 @@ public final class PluginProcessor for (PluginNode parent : node.getParents()) { - PluginWrapper wrapper = parent.getWrapper(); + InstalledPlugin wrapper = parent.getWrapper(); if (wrapper != null) { @@ -236,7 +236,7 @@ public final class PluginProcessor } - PluginWrapper plugin = + InstalledPlugin plugin = createPluginWrapper(createParentPluginClassLoader(classLoader, parents), smp); @@ -257,7 +257,7 @@ public final class PluginProcessor * * @throws IOException */ - private void appendPluginWrappers(Set plugins, + private void appendPluginWrappers(Set plugins, ClassLoader classLoader, List nodes) throws IOException { @@ -474,11 +474,11 @@ public final class PluginProcessor * * @throws IOException */ - private PluginWrapper createPluginWrapper(ClassLoader classLoader, - ExplodedSmp smp) + private InstalledPlugin createPluginWrapper(ClassLoader classLoader, + ExplodedSmp smp) throws IOException { - PluginWrapper wrapper = null; + InstalledPlugin wrapper = null; Path directory = smp.getPath(); Path descriptor = directory.resolve(PluginConstants.FILE_DESCRIPTOR); @@ -490,7 +490,7 @@ public final class PluginProcessor WebResourceLoader resourceLoader = createWebResourceLoader(directory); - wrapper = new PluginWrapper(plugin, cl, resourceLoader, directory); + wrapper = new InstalledPlugin(plugin, cl, resourceLoader, directory); } else { @@ -512,11 +512,11 @@ public final class PluginProcessor * * @throws IOException */ - private Set createPluginWrappers(ClassLoader classLoader, - List rootNodes) + private Set createPluginWrappers(ClassLoader classLoader, + List rootNodes) throws IOException { - Set plugins = Sets.newHashSet(); + Set plugins = Sets.newHashSet(); appendPluginWrappers(plugins, classLoader, rootNodes); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java index 0354ded11a..fb916ee5ec 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java @@ -87,8 +87,8 @@ public final class PluginsInternal * * @throws IOException */ - public static Set collectPlugins(ClassLoaderLifeCycle classLoaderLifeCycle, - Path directory) + public static Set collectPlugins(ClassLoaderLifeCycle classLoaderLifeCycle, + Path directory) throws IOException { PluginProcessor processor = new PluginProcessor(classLoaderLifeCycle, directory); @@ -159,7 +159,7 @@ public final class PluginsInternal * * @return */ - public static Iterable unwrap(Iterable wrapped) + public static Iterable unwrap(Iterable wrapped) { return Iterables.transform(wrapped, new Unwrap()); } @@ -188,7 +188,7 @@ public final class PluginsInternal * @version Enter version here..., 14/06/05 * @author Enter your name here... */ - private static class Unwrap implements Function + private static class Unwrap implements Function { /** @@ -200,7 +200,7 @@ public final class PluginsInternal * @return */ @Override - public Plugin apply(PluginWrapper wrapper) + public Plugin apply(InstalledPlugin wrapper) { return wrapper.getPlugin(); } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/UberClassLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/UberClassLoader.java index 311cb9e879..62b1073a85 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/UberClassLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/UberClassLoader.java @@ -65,7 +65,7 @@ public final class UberClassLoader extends ClassLoader * @param parent * @param plugins */ - public UberClassLoader(ClassLoader parent, Iterable plugins) + public UberClassLoader(ClassLoader parent, Iterable plugins) { super(parent); this.plugins = plugins; @@ -87,7 +87,7 @@ public final class UberClassLoader extends ClassLoader } private Class findClassInPlugins(String name) throws ClassNotFoundException { - for (PluginWrapper plugin : plugins) { + for (InstalledPlugin plugin : plugins) { Class clazz = findClass(plugin.getClassLoader(), name); if (clazz != null) { return clazz; @@ -119,7 +119,7 @@ public final class UberClassLoader extends ClassLoader { URL url = null; - for (PluginWrapper plugin : plugins) + for (InstalledPlugin plugin : plugins) { ClassLoader cl = plugin.getClassLoader(); @@ -149,7 +149,7 @@ public final class UberClassLoader extends ClassLoader { List urls = Lists.newArrayList(); - for (PluginWrapper plugin : plugins) + for (InstalledPlugin plugin : plugins) { ClassLoader cl = plugin.getClassLoader(); @@ -194,5 +194,5 @@ public final class UberClassLoader extends ClassLoader Maps.newConcurrentMap(); /** Field description */ - private final Iterable plugins; + private final Iterable plugins; } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index a81eadadb8..098a12880a 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -20,7 +20,7 @@ import sonia.scm.plugin.Plugin; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginState; -import sonia.scm.plugin.PluginWrapper; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.web.VndMediaType; import javax.inject.Provider; @@ -86,9 +86,9 @@ class InstalledPluginResourceTest { @Test void getInstalledPlugins() throws URISyntaxException, UnsupportedEncodingException { - PluginWrapper pluginWrapper = new PluginWrapper(null, null, null, null); - when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(pluginWrapper)); - when(collectionMapper.map(Collections.singletonList(pluginWrapper))).thenReturn(new MockedResultDto()); + InstalledPlugin installedPlugin = new InstalledPlugin(null, null, null, null); + when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(installedPlugin)); + when(collectionMapper.map(Collections.singletonList(installedPlugin))).thenReturn(new MockedResultDto()); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); request.accept(VndMediaType.PLUGIN_COLLECTION); @@ -107,12 +107,12 @@ class InstalledPluginResourceTest { pluginInformation.setName("pluginName"); pluginInformation.setState(PluginState.INSTALLED); Plugin plugin = new Plugin(2, pluginInformation, null, null, false, null); - PluginWrapper pluginWrapper = new PluginWrapper(plugin, null, null, null); - when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(pluginWrapper)); + InstalledPlugin installedPlugin = new InstalledPlugin(plugin, null, null, null); + when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(installedPlugin)); PluginDto pluginDto = new PluginDto(); pluginDto.setName("pluginName"); - when(mapper.map(pluginWrapper)).thenReturn(pluginDto); + when(mapper.map(installedPlugin)).thenReturn(pluginDto); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed/pluginName"); request.accept(VndMediaType.PLUGIN); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java index b2dafc8cfe..2dc195808e 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java @@ -170,7 +170,7 @@ public class UIRootResourceTest { assertTrue(response.getContentAsString().contains("/scm/my/bundle.js")); } - private void mockPlugins(PluginWrapper... plugins) { + private void mockPlugins(InstalledPlugin... plugins) { when(pluginLoader.getInstalledPlugins()).thenReturn(Lists.newArrayList(plugins)); } @@ -180,12 +180,12 @@ public class UIRootResourceTest { return new PluginResources(scripts, styles); } - private PluginWrapper mockPlugin(String id) { + private InstalledPlugin mockPlugin(String id) { return mockPlugin(id, id, null); } - private PluginWrapper mockPlugin(String id, String name, PluginResources pluginResources) { - PluginWrapper wrapper = mock(PluginWrapper.class); + private InstalledPlugin mockPlugin(String id, String name, PluginResources pluginResources) { + InstalledPlugin wrapper = mock(InstalledPlugin.class); when(wrapper.getId()).thenReturn(id); Plugin plugin = mock(Plugin.class); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java index 9e5ebccbfd..7cb534c7ba 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultUberWebResourceLoaderTest.java @@ -102,7 +102,7 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase public void testGetResourceFromCache() { DefaultUberWebResourceLoader resourceLoader = new DefaultUberWebResourceLoader(servletContext, - new ArrayList(), Stage.PRODUCTION); + new ArrayList(), Stage.PRODUCTION); resourceLoader.getCache().put("/myresource", GITHUB); @@ -131,8 +131,8 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase { File directory = temp.newFolder(); File file = file(directory, "myresource"); - PluginWrapper wrapper = createPluginWrapper(directory); - List plugins = Lists.newArrayList(wrapper); + InstalledPlugin wrapper = createPluginWrapper(directory); + List plugins = Lists.newArrayList(wrapper); WebResourceLoader resourceLoader = new DefaultUberWebResourceLoader(servletContext, plugins); @@ -170,8 +170,8 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase File directory = temp.newFolder(); File file = file(directory, "myresource"); - PluginWrapper wrapper = createPluginWrapper(directory); - List plugins = Lists.newArrayList(wrapper); + InstalledPlugin wrapper = createPluginWrapper(directory); + List plugins = Lists.newArrayList(wrapper); UberWebResourceLoader resourceLoader = new DefaultUberWebResourceLoader(servletContext, plugins); @@ -197,11 +197,11 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase WebResourceLoader loader = mock(WebResourceLoader.class); when(loader.getResource("/myresource")).thenReturn(url); - PluginWrapper pluginWrapper = mock(PluginWrapper.class); - when(pluginWrapper.getWebResourceLoader()).thenReturn(loader); + InstalledPlugin installedPlugin = mock(InstalledPlugin.class); + when(installedPlugin.getWebResourceLoader()).thenReturn(loader); WebResourceLoader resourceLoader = - new DefaultUberWebResourceLoader(servletContext, Lists.newArrayList(pluginWrapper)); + new DefaultUberWebResourceLoader(servletContext, Lists.newArrayList(installedPlugin)); assertNull(resourceLoader.getResource("/myresource")); } @@ -214,11 +214,11 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase WebResourceLoader loader = mock(WebResourceLoader.class); when(loader.getResource("/myresource")).thenReturn(url); - PluginWrapper pluginWrapper = mock(PluginWrapper.class); - when(pluginWrapper.getWebResourceLoader()).thenReturn(loader); + InstalledPlugin installedPlugin = mock(InstalledPlugin.class); + when(installedPlugin.getWebResourceLoader()).thenReturn(loader); UberWebResourceLoader resourceLoader = - new DefaultUberWebResourceLoader(servletContext, Lists.newArrayList(pluginWrapper)); + new DefaultUberWebResourceLoader(servletContext, Lists.newArrayList(installedPlugin)); List resources = resourceLoader.getResources("/myresource"); Assertions.assertThat(resources).isEmpty(); @@ -232,7 +232,7 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase * * @return */ - private PluginWrapper createPluginWrapper(File directory) + private InstalledPlugin createPluginWrapper(File directory) { return createPluginWrapper(directory.toPath()); } @@ -245,9 +245,9 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase * * @return */ - private PluginWrapper createPluginWrapper(Path directory) + private InstalledPlugin createPluginWrapper(Path directory) { - return new PluginWrapper(null, null, new PathWebResourceLoader(directory), + return new InstalledPlugin(null, null, new PathWebResourceLoader(directory), directory); } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java index 87e9cbf7b7..0fbf7eabbc 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java @@ -129,7 +129,7 @@ public class PluginProcessorTest { copySmp(PLUGIN_A); - PluginWrapper plugin = collectAndGetFirst(); + InstalledPlugin plugin = collectAndGetFirst(); assertThat(plugin.getId(), is(PLUGIN_A.id)); } @@ -145,15 +145,15 @@ public class PluginProcessorTest { copySmps(PLUGIN_A, PLUGIN_B); - Set plugins = collectPlugins(); + Set plugins = collectPlugins(); assertThat(plugins, hasSize(2)); - PluginWrapper a = findPlugin(plugins, PLUGIN_A.id); + InstalledPlugin a = findPlugin(plugins, PLUGIN_A.id); assertNotNull(a); - PluginWrapper b = findPlugin(plugins, PLUGIN_B.id); + InstalledPlugin b = findPlugin(plugins, PLUGIN_B.id); assertNotNull(b); } @@ -178,7 +178,7 @@ public class PluginProcessorTest { copySmp(PLUGIN_A); - PluginWrapper plugin = collectAndGetFirst(); + InstalledPlugin plugin = collectAndGetFirst(); ClassLoader cl = plugin.getClassLoader(); // load parent class @@ -216,9 +216,9 @@ public class PluginProcessorTest { copySmps(PLUGIN_A, PLUGIN_B); - Set plugins = collectPlugins(); + Set plugins = collectPlugins(); - PluginWrapper plugin = findPlugin(plugins, PLUGIN_B.id); + InstalledPlugin plugin = findPlugin(plugins, PLUGIN_B.id); ClassLoader cl = plugin.getClassLoader(); // load parent class @@ -247,7 +247,7 @@ public class PluginProcessorTest { copySmp(PLUGIN_A); - PluginWrapper plugin = collectAndGetFirst(); + InstalledPlugin plugin = collectAndGetFirst(); WebResourceLoader wrl = plugin.getWebResourceLoader(); assertNotNull(wrl); @@ -269,7 +269,7 @@ public class PluginProcessorTest { copySmp(PLUGIN_F_1_0_0); - PluginWrapper plugin = collectAndGetFirst(); + InstalledPlugin plugin = collectAndGetFirst(); assertThat(plugin.getId(), is(PLUGIN_F_1_0_0.id)); copySmp(PLUGIN_F_1_0_1); @@ -302,9 +302,9 @@ public class PluginProcessorTest * * @throws IOException */ - private PluginWrapper collectAndGetFirst() throws IOException + private InstalledPlugin collectAndGetFirst() throws IOException { - Set plugins = collectPlugins(); + Set plugins = collectPlugins(); assertThat(plugins, hasSize(1)); @@ -319,7 +319,7 @@ public class PluginProcessorTest * * @throws IOException */ - private Set collectPlugins() throws IOException + private Set collectPlugins() throws IOException { return processor.collectPlugins(PluginProcessorTest.class.getClassLoader()); } @@ -368,14 +368,14 @@ public class PluginProcessorTest * * @return */ - private PluginWrapper findPlugin(Iterable plugin, - final String id) + private InstalledPlugin findPlugin(Iterable plugin, + final String id) { - return Iterables.find(plugin, new Predicate() + return Iterables.find(plugin, new Predicate() { @Override - public boolean apply(PluginWrapper input) + public boolean apply(InstalledPlugin input) { return id.equals(input.getId()); } From 1a01216f62416f0f8ae198f222b89fbcd2224a04 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 08:05:41 +0200 Subject: [PATCH 03/27] renamed Plugin to InstalledPluginDescriptor and added PluginDescriptor interface --- .../java/sonia/scm/plugin/InstalledPlugin.java | 8 ++++---- ...lugin.java => InstalledPluginDescriptor.java} | 15 +++++++++------ .../java/sonia/scm/plugin/PluginDescriptor.java | 13 +++++++++++++ .../src/main/java/sonia/scm/plugin/Plugins.java | 10 +++++----- .../main/java/sonia/scm/plugin/SmpArchive.java | 8 ++++---- .../java/sonia/scm/plugin/SmpArchiveTest.java | 2 +- .../v2/resources/AvailablePluginResource.java | 4 ++-- .../v2/resources/InstalledPluginResource.java | 4 ++-- .../sonia/scm/lifecycle/PluginBootstrap.java | 4 ++-- .../sonia/scm/plugin/DefaultPluginLoader.java | 4 ++-- .../sonia/scm/plugin/DefaultPluginManager.java | 16 ++++++++-------- .../main/java/sonia/scm/plugin/ExplodedSmp.java | 6 +++--- .../java/sonia/scm/plugin/PluginProcessor.java | 10 +++++----- .../main/java/sonia/scm/plugin/PluginTree.java | 2 +- .../java/sonia/scm/plugin/PluginsInternal.java | 8 ++++---- .../resources/InstalledPluginResourceTest.java | 4 ++-- .../scm/api/v2/resources/UIRootResourceTest.java | 2 +- .../java/sonia/scm/plugin/ExplodedSmpTest.java | 2 +- .../java/sonia/scm/plugin/PluginTreeTest.java | 10 +++++----- 19 files changed, 74 insertions(+), 58 deletions(-) rename scm-core/src/main/java/sonia/scm/plugin/{Plugin.java => InstalledPluginDescriptor.java} (92%) create mode 100644 scm-core/src/main/java/sonia/scm/plugin/PluginDescriptor.java diff --git a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java index 8e93953074..19f9584408 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java @@ -36,7 +36,7 @@ package sonia.scm.plugin; import java.nio.file.Path; /** - * Wrapper for a {@link Plugin}. The wrapper holds the directory, + * Wrapper for a {@link InstalledPluginDescriptor}. The wrapper holds the directory, * {@link ClassLoader} and {@link WebResourceLoader} of a plugin. * * @author Sebastian Sdorra @@ -53,7 +53,7 @@ public final class InstalledPlugin * @param webResourceLoader web resource loader * @param directory plugin directory */ - public InstalledPlugin(Plugin plugin, ClassLoader classLoader, + public InstalledPlugin(InstalledPluginDescriptor plugin, ClassLoader classLoader, WebResourceLoader webResourceLoader, Path directory) { this.plugin = plugin; @@ -103,7 +103,7 @@ public final class InstalledPlugin * * @return plugin */ - public Plugin getPlugin() + public InstalledPluginDescriptor getPlugin() { return plugin; } @@ -128,7 +128,7 @@ public final class InstalledPlugin private final Path directory; /** plugin */ - private final Plugin plugin; + private final InstalledPluginDescriptor plugin; /** plugin web resource loader */ private final WebResourceLoader webResourceLoader; diff --git a/scm-core/src/main/java/sonia/scm/plugin/Plugin.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java similarity index 92% rename from scm-core/src/main/java/sonia/scm/plugin/Plugin.java rename to scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java index e8fd166e78..28504650bd 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/Plugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java @@ -54,14 +54,14 @@ import java.util.Set; */ @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) -public final class Plugin extends ScmModule +public final class InstalledPluginDescriptor extends ScmModule implements PluginDescriptor { /** * Constructs ... * */ - Plugin() {} + InstalledPluginDescriptor() {} /** * Constructs ... @@ -74,9 +74,9 @@ public final class Plugin extends ScmModule * @param childFirstClassLoader * @param dependencies */ - public Plugin(int scmVersion, PluginInformation information, - PluginResources resources, PluginCondition condition, - boolean childFirstClassLoader, Set dependencies) + public InstalledPluginDescriptor(int scmVersion, PluginInformation information, + PluginResources resources, PluginCondition condition, + boolean childFirstClassLoader, Set dependencies) { this.scmVersion = scmVersion; this.information = information; @@ -109,7 +109,7 @@ public final class Plugin extends ScmModule return false; } - final Plugin other = (Plugin) obj; + final InstalledPluginDescriptor other = (InstalledPluginDescriptor) obj; return Objects.equal(scmVersion, other.scmVersion) && Objects.equal(condition, other.condition) @@ -161,6 +161,7 @@ public final class Plugin extends ScmModule * * @return */ + @Override public PluginCondition getCondition() { return condition; @@ -174,6 +175,7 @@ public final class Plugin extends ScmModule * * @since 2.0.0 */ + @Override public Set getDependencies() { if (dependencies == null) @@ -190,6 +192,7 @@ public final class Plugin extends ScmModule * * @return */ + @Override public PluginInformation getInformation() { return information; diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginDescriptor.java b/scm-core/src/main/java/sonia/scm/plugin/PluginDescriptor.java new file mode 100644 index 0000000000..6e800faff0 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginDescriptor.java @@ -0,0 +1,13 @@ +package sonia.scm.plugin; + +import java.util.Set; + +public interface PluginDescriptor { + + PluginInformation getInformation(); + + PluginCondition getCondition(); + + Set getDependencies(); + +} diff --git a/scm-core/src/main/java/sonia/scm/plugin/Plugins.java b/scm-core/src/main/java/sonia/scm/plugin/Plugins.java index 6359850712..f33a254581 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/Plugins.java +++ b/scm-core/src/main/java/sonia/scm/plugin/Plugins.java @@ -65,7 +65,7 @@ public final class Plugins { try { - context = JAXBContext.newInstance(Plugin.class, ScmModule.class); + context = JAXBContext.newInstance(InstalledPluginDescriptor.class, ScmModule.class); } catch (JAXBException ex) { @@ -91,7 +91,7 @@ public final class Plugins * * @return */ - public static Plugin parsePluginDescriptor(Path path) + public static InstalledPluginDescriptor parsePluginDescriptor(Path path) { return parsePluginDescriptor(Files.asByteSource(path.toFile())); } @@ -104,15 +104,15 @@ public final class Plugins * * @return */ - public static Plugin parsePluginDescriptor(ByteSource data) + public static InstalledPluginDescriptor parsePluginDescriptor(ByteSource data) { Preconditions.checkNotNull(data, "data parameter is required"); - Plugin plugin; + InstalledPluginDescriptor plugin; try (InputStream stream = data.openStream()) { - plugin = (Plugin) context.createUnmarshaller().unmarshal(stream); + plugin = (InstalledPluginDescriptor) context.createUnmarshaller().unmarshal(stream); } catch (JAXBException ex) { 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 f674bdd2ba..e1ea622bdf 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java +++ b/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java @@ -206,7 +206,7 @@ public final class SmpArchive * * @throws IOException */ - public Plugin getPlugin() throws IOException + public InstalledPluginDescriptor getPlugin() throws IOException { if (plugin == null) { @@ -245,9 +245,9 @@ public final class SmpArchive * * @throws IOException */ - private Plugin createPlugin() throws IOException + private InstalledPluginDescriptor createPlugin() throws IOException { - Plugin p = null; + InstalledPluginDescriptor p = null; NonClosingZipInputStream zis = null; try @@ -412,5 +412,5 @@ public final class SmpArchive private final ByteSource archive; /** Field description */ - private Plugin plugin; + private InstalledPluginDescriptor plugin; } diff --git a/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java b/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java index d7f4ecf515..07e182216f 100644 --- a/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java +++ b/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java @@ -113,7 +113,7 @@ public class SmpArchiveTest public void testGetPlugin() throws IOException { File archive = createArchive("sonia.sample", "1.0"); - Plugin plugin = SmpArchive.create(archive).getPlugin(); + InstalledPluginDescriptor plugin = SmpArchive.create(archive).getPlugin(); assertNotNull(plugin); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index 6d5711133f..7ab49306fd 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.plugin.Plugin; +import sonia.scm.plugin.InstalledPluginDescriptor; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; @@ -83,7 +83,7 @@ public class AvailablePluginResource { if (plugin.isPresent()) { return Response.ok(mapper.map(plugin.get())).build(); } else { - throw notFound(entity(Plugin.class, name)); + throw notFound(entity(InstalledPluginDescriptor.class, name)); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java index 230b34171b..e18f6772dc 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.plugin.Plugin; +import sonia.scm.plugin.InstalledPluginDescriptor; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; @@ -83,7 +83,7 @@ public class InstalledPluginResource { if (pluginDto.isPresent()) { return Response.ok(pluginDto.get()).build(); } else { - throw notFound(entity(Plugin.class, name)); + throw notFound(entity(InstalledPluginDescriptor.class, name)); } } } diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java index 3d7e6ec0b2..ff8c28f51d 100644 --- a/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java +++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/PluginBootstrap.java @@ -9,7 +9,7 @@ import sonia.scm.SCMContext; import sonia.scm.lifecycle.classloading.ClassLoaderLifeCycle; import sonia.scm.migration.UpdateException; import sonia.scm.plugin.DefaultPluginLoader; -import sonia.scm.plugin.Plugin; +import sonia.scm.plugin.InstalledPluginDescriptor; import sonia.scm.plugin.PluginException; import sonia.scm.plugin.PluginLoadException; import sonia.scm.plugin.PluginLoader; @@ -105,7 +105,7 @@ public final class PluginBootstrap { PluginIndexEntry entry) throws IOException { URL url = context.getResource(PLUGIN_DIRECTORY.concat(entry.getName())); SmpArchive archive = SmpArchive.create(url); - Plugin plugin = archive.getPlugin(); + InstalledPluginDescriptor plugin = archive.getPlugin(); File directory = PluginsInternal.createPluginDirectory(pluginDirectory, plugin); File checksumFile = PluginsInternal.getChecksumFile(directory); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java index 1e4a4b92d7..5612b0395c 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java @@ -95,7 +95,7 @@ public class DefaultPluginLoader implements PluginLoader try { JAXBContext context = JAXBContext.newInstance(ScmModule.class, - Plugin.class); + InstalledPluginDescriptor.class); modules = getInstalled(parent, context, PATH_MODULECONFIG); @@ -178,7 +178,7 @@ public class DefaultPluginLoader implements PluginLoader * * @return */ - private Iterable unwrap() + private Iterable unwrap() { return PluginsInternal.unwrap(installedPlugins); } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 70cbab23ff..509870abf8 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -131,7 +131,7 @@ public class DefaultPluginManager implements PluginManager for (InstalledPlugin wrapper : pluginLoader.getInstalledPlugins()) { - Plugin plugin = wrapper.getPlugin(); + InstalledPluginDescriptor plugin = wrapper.getPlugin(); PluginInformation info = plugin.getInformation(); if ((info != null) && info.isValid()) @@ -192,7 +192,7 @@ public class DefaultPluginManager implements PluginManager plugin.setState(PluginState.INSTALLED); // ugly workaround - Plugin newPlugin = new Plugin(); + InstalledPluginDescriptor newPlugin = new InstalledPluginDescriptor(); // TODO check // newPlugin.setInformation(plugin); @@ -220,8 +220,8 @@ public class DefaultPluginManager implements PluginManager { new ZipUnArchiver().extractArchive(packageStream, tempDirectory); - Plugin plugin = JAXB.unmarshal(new File(tempDirectory, "plugin.xml"), - Plugin.class); + InstalledPluginDescriptor plugin = JAXB.unmarshal(new File(tempDirectory, "plugin.xml"), + InstalledPluginDescriptor.class); PluginCondition condition = plugin.getCondition(); @@ -262,7 +262,7 @@ public class DefaultPluginManager implements PluginManager { PluginPermissions.manage().check(); - Plugin plugin = installedPlugins.get(id); + InstalledPluginDescriptor plugin = installedPlugins.get(id); if (plugin == null) { @@ -457,7 +457,7 @@ public class DefaultPluginManager implements PluginManager Set infoSet = new LinkedHashSet<>(); - for (Plugin plugin : installedPlugins.values()) + for (InstalledPluginDescriptor plugin : installedPlugins.values()) { infoSet.add(plugin.getInformation()); } @@ -647,7 +647,7 @@ public class DefaultPluginManager implements PluginManager { boolean core = false; - for (Plugin installedPlugin : installedPlugins.values()) + for (InstalledPluginDescriptor installedPlugin : installedPlugins.values()) { PluginInformation installed = installedPlugin.getInformation(); @@ -715,5 +715,5 @@ public class DefaultPluginManager implements PluginManager private final SCMContextProvider context; /** Field description */ - private final Map installedPlugins; + private final Map installedPlugins; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java b/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java index 372470df14..ee2514dc3e 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java @@ -63,7 +63,7 @@ public final class ExplodedSmp implements Comparable * @param path * @param plugin */ - ExplodedSmp(Path path, Plugin plugin) + ExplodedSmp(Path path, InstalledPluginDescriptor plugin) { logger.trace("create exploded scm for plugin {} and dependencies {}", plugin.getInformation().getName(), plugin.getDependencies()); this.path = path; @@ -163,7 +163,7 @@ public final class ExplodedSmp implements Comparable * * @return plugin descriptor */ - public Plugin getPlugin() + public InstalledPluginDescriptor getPlugin() { return plugin; } @@ -202,5 +202,5 @@ public final class ExplodedSmp implements Comparable private final Path path; /** plugin object */ - private final Plugin plugin; + private final InstalledPluginDescriptor plugin; } 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 3a168f43d2..ed1bc7643a 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -123,7 +123,7 @@ public final class PluginProcessor try { - this.context = JAXBContext.newInstance(Plugin.class); + this.context = JAXBContext.newInstance(InstalledPluginDescriptor.class); } catch (JAXBException ex) { @@ -371,7 +371,7 @@ public final class PluginProcessor ClassLoader classLoader; URL[] urlArray = urls.toArray(new URL[urls.size()]); - Plugin plugin = smp.getPlugin(); + InstalledPluginDescriptor plugin = smp.getPlugin(); String id = plugin.getInformation().getName(false); @@ -441,7 +441,7 @@ public final class PluginProcessor * * @return */ - private Plugin createPlugin(ClassLoader classLoader, Path descriptor) + private InstalledPluginDescriptor createPlugin(ClassLoader classLoader, Path descriptor) { ClassLoader ctxcl = Thread.currentThread().getContextClassLoader(); @@ -449,7 +449,7 @@ public final class PluginProcessor try { - return (Plugin) context.createUnmarshaller().unmarshal( + return (InstalledPluginDescriptor) context.createUnmarshaller().unmarshal( descriptor.toFile()); } catch (JAXBException ex) @@ -486,7 +486,7 @@ public final class PluginProcessor { ClassLoader cl = createClassLoader(classLoader, smp); - Plugin plugin = createPlugin(cl, descriptor); + InstalledPluginDescriptor plugin = createPlugin(cl, descriptor); WebResourceLoader resourceLoader = createWebResourceLoader(directory); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginTree.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginTree.java index 7e57fb3d57..bd338c0741 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginTree.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginTree.java @@ -86,7 +86,7 @@ public final class PluginTree for (ExplodedSmp smp : smpOrdered) { - Plugin plugin = smp.getPlugin(); + InstalledPluginDescriptor plugin = smp.getPlugin(); if (plugin.getScmVersion() != SCM_VERSION) { diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java index fb916ee5ec..9ac8bcbe71 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java @@ -105,7 +105,7 @@ public final class PluginsInternal * * @return */ - public static File createPluginDirectory(File parent, Plugin plugin) + public static File createPluginDirectory(File parent, InstalledPluginDescriptor plugin) { PluginInformation info = plugin.getInformation(); @@ -159,7 +159,7 @@ public final class PluginsInternal * * @return */ - public static Iterable unwrap(Iterable wrapped) + public static Iterable unwrap(Iterable wrapped) { return Iterables.transform(wrapped, new Unwrap()); } @@ -188,7 +188,7 @@ public final class PluginsInternal * @version Enter version here..., 14/06/05 * @author Enter your name here... */ - private static class Unwrap implements Function + private static class Unwrap implements Function { /** @@ -200,7 +200,7 @@ public final class PluginsInternal * @return */ @Override - public Plugin apply(InstalledPlugin wrapper) + public InstalledPluginDescriptor apply(InstalledPlugin wrapper) { return wrapper.getPlugin(); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index 098a12880a..ce781c7c32 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import sonia.scm.plugin.Plugin; +import sonia.scm.plugin.InstalledPluginDescriptor; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginState; @@ -106,7 +106,7 @@ class InstalledPluginResourceTest { pluginInformation.setVersion("2.0.0"); pluginInformation.setName("pluginName"); pluginInformation.setState(PluginState.INSTALLED); - Plugin plugin = new Plugin(2, pluginInformation, null, null, false, null); + InstalledPluginDescriptor plugin = new InstalledPluginDescriptor(2, pluginInformation, null, null, false, null); InstalledPlugin installedPlugin = new InstalledPlugin(plugin, null, null, null); when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(installedPlugin)); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java index 2dc195808e..7fd605f24c 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java @@ -188,7 +188,7 @@ public class UIRootResourceTest { InstalledPlugin wrapper = mock(InstalledPlugin.class); when(wrapper.getId()).thenReturn(id); - Plugin plugin = mock(Plugin.class); + InstalledPluginDescriptor plugin = mock(InstalledPluginDescriptor.class); when(wrapper.getPlugin()).thenReturn(plugin); when(plugin.getResources()).thenReturn(pluginResources); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java index 601725d938..090d903f49 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java @@ -133,7 +133,7 @@ public class ExplodedSmpTest info.setName(name); info.setVersion(version); - Plugin plugin = new Plugin(2, info, null, null, false, + InstalledPluginDescriptor plugin = new InstalledPluginDescriptor(2, info, null, null, false, Sets.newSet(dependencies)); return new ExplodedSmp(null, plugin); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java index 0115f4510e..72c48d4d16 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java @@ -71,7 +71,7 @@ public class PluginTreeTest { PluginCondition condition = new PluginCondition("999", new ArrayList(), "hit"); - Plugin plugin = new Plugin(2, createInfo("a", "1"), null, condition, + InstalledPluginDescriptor plugin = new InstalledPluginDescriptor(2, createInfo("a", "1"), null, condition, false, null); ExplodedSmp smp = createSmp(plugin); @@ -114,7 +114,7 @@ public class PluginTreeTest @Test(expected = PluginException.class) public void testScmVersion() throws IOException { - Plugin plugin = new Plugin(1, createInfo("a", "1"), null, null, false, + InstalledPluginDescriptor plugin = new InstalledPluginDescriptor(1, createInfo("a", "1"), null, null, false, null); ExplodedSmp smp = createSmp(plugin); @@ -182,7 +182,7 @@ public class PluginTreeTest * * @throws IOException */ - private ExplodedSmp createSmp(Plugin plugin) throws IOException + private ExplodedSmp createSmp(InstalledPluginDescriptor plugin) throws IOException { return new ExplodedSmp(tempFolder.newFile().toPath(), plugin); } @@ -199,7 +199,7 @@ public class PluginTreeTest */ private ExplodedSmp createSmp(String name) throws IOException { - return createSmp(new Plugin(2, createInfo(name, "1.0.0"), null, null, + return createSmp(new InstalledPluginDescriptor(2, createInfo(name, "1.0.0"), null, null, false, null)); } @@ -225,7 +225,7 @@ public class PluginTreeTest dependencySet.add(d); } - Plugin plugin = new Plugin(2, createInfo(name, "1"), null, null, + InstalledPluginDescriptor plugin = new InstalledPluginDescriptor(2, createInfo(name, "1"), null, null, false, dependencySet); return createSmp(plugin); From 0aaec1174a4fb28c69e1e7358acee0acfc4ece34 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 08:10:30 +0200 Subject: [PATCH 04/27] introduce Plugin interface --- .../sonia/scm/plugin/InstalledPlugin.java | 26 ++++++++++++------- .../main/java/sonia/scm/plugin/Plugin.java | 8 ++++++ .../v2/resources/InstalledPluginResource.java | 2 +- .../scm/api/v2/resources/PluginDtoMapper.java | 2 +- .../api/v2/resources/UIPluginDtoMapper.java | 4 +-- .../api/v2/resources/UIPluginResource.java | 2 +- .../scm/plugin/DefaultPluginManager.java | 2 +- .../sonia/scm/plugin/PluginsInternal.java | 2 +- .../api/v2/resources/UIRootResourceTest.java | 2 +- 9 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/plugin/Plugin.java diff --git a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java index 19f9584408..fc1fbac94a 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java @@ -42,21 +42,21 @@ import java.nio.file.Path; * @author Sebastian Sdorra * @since 2.0.0 */ -public final class InstalledPlugin +public final class InstalledPlugin implements Plugin { /** * Constructs a new plugin wrapper. * - * @param plugin wrapped plugin + * @param descriptor wrapped plugin * @param classLoader plugin class loader * @param webResourceLoader web resource loader * @param directory plugin directory */ - public InstalledPlugin(InstalledPluginDescriptor plugin, ClassLoader classLoader, + public InstalledPlugin(InstalledPluginDescriptor descriptor, ClassLoader classLoader, WebResourceLoader webResourceLoader, Path directory) { - this.plugin = plugin; + this.descriptor = descriptor; this.classLoader = classLoader; this.webResourceLoader = webResourceLoader; this.directory = directory; @@ -94,18 +94,19 @@ public final class InstalledPlugin */ public String getId() { - return plugin.getInformation().getId(); + return descriptor.getInformation().getId(); } /** - * Returns the plugin. + * Returns the plugin descriptor. * * - * @return plugin + * @return plugin descriptor */ - public InstalledPluginDescriptor getPlugin() + @Override + public InstalledPluginDescriptor getDescriptor() { - return plugin; + return descriptor; } /** @@ -119,6 +120,11 @@ public final class InstalledPlugin return webResourceLoader; } + @Override + public PluginState getState() { + return PluginState.INSTALLED; + } + //~--- fields --------------------------------------------------------------- /** plugin class loader */ @@ -128,7 +134,7 @@ public final class InstalledPlugin private final Path directory; /** plugin */ - private final InstalledPluginDescriptor plugin; + private final InstalledPluginDescriptor descriptor; /** plugin web resource loader */ private final WebResourceLoader webResourceLoader; diff --git a/scm-core/src/main/java/sonia/scm/plugin/Plugin.java b/scm-core/src/main/java/sonia/scm/plugin/Plugin.java new file mode 100644 index 0000000000..e39d23c046 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/Plugin.java @@ -0,0 +1,8 @@ +package sonia.scm.plugin; + +public interface Plugin { + + PluginDescriptor getDescriptor(); + PluginState getState(); + +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java index e18f6772dc..66347814a3 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java @@ -77,7 +77,7 @@ public class InstalledPluginResource { PluginPermissions.read().check(); Optional pluginDto = pluginLoader.getInstalledPlugins() .stream() - .filter(plugin -> name.equals(plugin.getPlugin().getInformation().getName())) + .filter(plugin -> name.equals(plugin.getDescriptor().getInformation().getName())) .map(mapper::map) .findFirst(); if (pluginDto.isPresent()) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index 4967c55b31..4710fa943c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -21,7 +21,7 @@ public abstract class PluginDtoMapper { private ResourceLinks resourceLinks; public PluginDto map(InstalledPlugin plugin) { - return map(plugin.getPlugin().getInformation()); + return map(plugin.getDescriptor().getInformation()); } public abstract PluginDto map(PluginInformation plugin); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java index 8a2b6cb0c1..5eecaa0561 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginDtoMapper.java @@ -27,7 +27,7 @@ public class UIPluginDtoMapper { public UIPluginDto map(InstalledPlugin plugin) { UIPluginDto dto = new UIPluginDto( - plugin.getPlugin().getInformation().getName(), + plugin.getDescriptor().getInformation().getName(), getScriptResources(plugin) ); @@ -41,7 +41,7 @@ public class UIPluginDtoMapper { } private Set getScriptResources(InstalledPlugin wrapper) { - Set scriptResources = wrapper.getPlugin().getResources().getScriptResources(); + Set scriptResources = wrapper.getDescriptor().getResources().getScriptResources(); if (scriptResources != null) { return scriptResources.stream() .map(this::addContextPath) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java index d34bcbe3ba..1c779653a0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java @@ -86,7 +86,7 @@ public class UIPluginResource { } private boolean filter(InstalledPlugin plugin) { - return plugin.getPlugin().getResources() != null; + return plugin.getDescriptor().getResources() != null; } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 509870abf8..c19c19b377 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -131,7 +131,7 @@ public class DefaultPluginManager implements PluginManager for (InstalledPlugin wrapper : pluginLoader.getInstalledPlugins()) { - InstalledPluginDescriptor plugin = wrapper.getPlugin(); + InstalledPluginDescriptor plugin = wrapper.getDescriptor(); PluginInformation info = plugin.getInformation(); if ((info != null) && info.isValid()) diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java index 9ac8bcbe71..242086aa85 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java @@ -202,7 +202,7 @@ public final class PluginsInternal @Override public InstalledPluginDescriptor apply(InstalledPlugin wrapper) { - return wrapper.getPlugin(); + return wrapper.getDescriptor(); } } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java index 7fd605f24c..4987dec644 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java @@ -189,7 +189,7 @@ public class UIRootResourceTest { when(wrapper.getId()).thenReturn(id); InstalledPluginDescriptor plugin = mock(InstalledPluginDescriptor.class); - when(wrapper.getPlugin()).thenReturn(plugin); + when(wrapper.getDescriptor()).thenReturn(plugin); when(plugin.getResources()).thenReturn(pluginResources); PluginInformation information = mock(PluginInformation.class); From 3f1521bccaf3c369c0509269cc01030155780966 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 10:33:57 +0200 Subject: [PATCH 05/27] create new simplified PluginManager API --- .../sonia/scm/plugin/AvailablePlugin.java | 20 + .../scm/plugin/AvailablePluginDescriptor.java | 34 + .../scm/plugin/InstalledPluginDescriptor.java | 3 +- .../java/sonia/scm/plugin/PluginCenter.java | 120 ---- .../sonia/scm/plugin/PluginInformation.java | 2 - .../plugin/PluginInformationComparator.java | 101 --- .../java/sonia/scm/plugin/PluginManager.java | 109 +-- .../sonia/scm/plugin/PluginRepository.java | 160 ----- .../scm/plugin/StatePluginPredicate.java | 78 -- .../v2/resources/AvailablePluginResource.java | 29 +- .../v2/resources/InstalledPluginResource.java | 22 +- .../resources/PluginDtoCollectionMapper.java | 5 +- .../scm/api/v2/resources/PluginDtoMapper.java | 24 +- .../scm/plugin/DefaultPluginManager.java | 668 +----------------- .../scm/plugin/OverviewPluginPredicate.java | 64 -- .../sonia/scm/plugin/PluginProcessor.java | 67 +- .../AvailablePluginResourceTest.java | 39 +- .../InstalledPluginResourceTest.java | 40 +- .../api/v2/resources/PluginDtoMapperTest.java | 45 +- 19 files changed, 220 insertions(+), 1410 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/plugin/AvailablePlugin.java create mode 100644 scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java delete mode 100644 scm-core/src/main/java/sonia/scm/plugin/PluginCenter.java delete mode 100644 scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java delete mode 100644 scm-core/src/main/java/sonia/scm/plugin/PluginRepository.java delete mode 100644 scm-core/src/main/java/sonia/scm/plugin/StatePluginPredicate.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/OverviewPluginPredicate.java diff --git a/scm-core/src/main/java/sonia/scm/plugin/AvailablePlugin.java b/scm-core/src/main/java/sonia/scm/plugin/AvailablePlugin.java new file mode 100644 index 0000000000..6596fa4751 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/AvailablePlugin.java @@ -0,0 +1,20 @@ +package sonia.scm.plugin; + +public class AvailablePlugin implements Plugin { + + private final AvailablePluginDescriptor pluginDescriptor; + + public AvailablePlugin(AvailablePluginDescriptor pluginDescriptor) { + this.pluginDescriptor = pluginDescriptor; + } + + @Override + public AvailablePluginDescriptor getDescriptor() { + return pluginDescriptor; + } + + @Override + public PluginState getState() { + return PluginState.AVAILABLE; + } +} diff --git a/scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java b/scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java new file mode 100644 index 0000000000..b7e7b5e282 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java @@ -0,0 +1,34 @@ +package sonia.scm.plugin; + +import java.util.Set; + +/** + * @since 2.0.0 + */ +public class AvailablePluginDescriptor implements PluginDescriptor { + + private final PluginInformation information; + private final PluginCondition condition; + private final Set dependencies; + + public AvailablePluginDescriptor(PluginInformation information, PluginCondition condition, Set dependencies) { + this.information = information; + this.condition = condition; + this.dependencies = dependencies; + } + + @Override + public PluginInformation getInformation() { + return information; + } + + @Override + public PluginCondition getCondition() { + return condition; + } + + @Override + public Set getDependencies() { + return dependencies; + } +} diff --git a/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java index 28504650bd..88ae4c5dff 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java @@ -52,7 +52,7 @@ import java.util.Set; * * @author Sebastian Sdorra */ -@XmlRootElement +@XmlRootElement(name = "plugin") @XmlAccessorType(XmlAccessType.FIELD) public final class InstalledPluginDescriptor extends ScmModule implements PluginDescriptor { @@ -247,6 +247,7 @@ public final class InstalledPluginDescriptor extends ScmModule implements Plugin private Set dependencies; /** Field description */ + @XmlElement(name = "information") private PluginInformation information; /** Field description */ diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginCenter.java b/scm-core/src/main/java/sonia/scm/plugin/PluginCenter.java deleted file mode 100644 index e1598e0490..0000000000 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginCenter.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.plugin; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.Serializable; - -import java.util.HashSet; -import java.util.Set; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * - * @author Sebastian Sdorra - */ -@XmlRootElement(name = "plugin-center") -@XmlAccessorType(XmlAccessType.FIELD) -public class PluginCenter implements Serializable -{ - - /** Field description */ - private static final long serialVersionUID = -6414175308610267397L; - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public Set getPlugins() - { - return plugins; - } - - /** - * Method description - * - * - * @return - */ - public Set getRepositories() - { - return repositories; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param plugins - */ - public void setPlugins(Set plugins) - { - this.plugins = plugins; - } - - /** - * Method description - * - * - * @param repositories - */ - public void setRepositories(Set repositories) - { - this.repositories = repositories; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - @XmlElement(name = "plugin") - @XmlElementWrapper(name = "plugins") - private Set plugins = new HashSet(); - - /** Field description */ - @XmlElement(name = "repository") - @XmlElementWrapper(name = "repositories") - private Set repositories = new HashSet(); -} diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 22911041d4..a9ae3b07f6 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -71,7 +71,6 @@ public class PluginInformation implements PermissionObject, Validateable, Clonea private String category; private String avatarUrl; private PluginCondition condition; - private PluginState state; @Override public PluginInformation clone() { @@ -83,7 +82,6 @@ public class PluginInformation implements PermissionObject, Validateable, Clonea clone.setAuthor(author); clone.setCategory(category); clone.setAvatarUrl(avatarUrl); - clone.setState(state); if (condition != null) { clone.setCondition(condition.clone()); } diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java deleted file mode 100644 index 5443b2328d..0000000000 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.plugin; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.util.Util; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.Serializable; - -import java.util.Comparator; - -/** - * - * @author Sebastian Sdorra - * @since 1.6 - */ -public class PluginInformationComparator - implements Comparator, Serializable -{ - - /** Field description */ - public static final PluginInformationComparator INSTANCE = - new PluginInformationComparator(); - - /** Field description */ - private static final long serialVersionUID = -8339752498853225668L; - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param plugin - * @param other - * - * @return - */ - @Override - public int compare(PluginInformation plugin, PluginInformation other) - { - int result = 0; - - result = Util.compare(plugin.getName(), other.getName()); - - if (result == 0) - { - PluginState state = plugin.getState(); - PluginState otherState = other.getState(); - - if ((state != null) && (otherState != null)) - { - result = state.getCompareValue() - otherState.getCompareValue(); - } - else if ((state == null) && (otherState != null)) - { - result = 1; - } - else if ((state != null) && (otherState == null)) - { - result = -1; - } - } - - return result; - } -} diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java b/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java index b1ec502fc4..ad9045544c 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java @@ -33,113 +33,50 @@ package sonia.scm.plugin; -//~--- JDK imports ------------------------------------------------------------ - -import com.google.common.base.Predicate; -import java.io.IOException; -import java.io.InputStream; - -import java.util.Collection; +import java.util.List; +import java.util.Optional; /** + * The plugin manager is responsible for plugin related tasks, such as install, uninstall or updating. * * @author Sebastian Sdorra */ -public interface PluginManager -{ +public interface PluginManager { /** - * Method description - * + * Returns the available plugin with the given name. + * @param name of plugin + * @return optional available plugin. */ - public void clearCache(); + Optional getAvailable(String name); /** - * Method description - * - * - * @param id + * Returns the installed plugin with the given name. + * @param name of plugin + * @return optional installed plugin. */ - public void install(String id); + Optional getInstalled(String name); + /** - * Installs a plugin package from a inputstream. + * Returns all installed plugins. * - * - * @param packageStream package input stream - * - * @throws IOException - * @since 1.21 + * @return a list of installed plugins. */ - public void installPackage(InputStream packageStream) throws IOException; + List getInstalled(); /** - * Method description + * Returns all available plugins. The list contains the plugins which are loaded from the plugin center, but without + * the installed plugins. * - * - * @param id + * @return a list of available plugins. */ - public void uninstall(String id); + List getAvailable(); /** - * Method description + * Installs the plugin with the given name from the list of available plugins. * - * - * @param id + * @param name plugin name */ - public void update(String id); - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param id - * - * @return - */ - public PluginInformation get(String id); - - /** - * Method description - * - * - * @param filter - * - * @return - */ - public Collection get(Predicate filter); - - /** - * Method description - * - * - * @return - */ - public Collection getAll(); - - /** - * Method description - * - * - * @return - */ - public Collection getAvailable(); - - /** - * Method description - * - * - * @return - */ - public Collection getAvailableUpdates(); - - /** - * Method description - * - * - * @return - */ - public Collection getInstalled(); + void install(String name); } diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginRepository.java b/scm-core/src/main/java/sonia/scm/plugin/PluginRepository.java deleted file mode 100644 index 1d4cc07338..0000000000 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginRepository.java +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.plugin; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.base.MoreObjects; -import com.google.common.base.Objects; - -import java.io.Serializable; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * - * @author Sebastian Sdorra - */ -public class PluginRepository implements Serializable -{ - - /** Field description */ - private static final long serialVersionUID = -9504354306304731L; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - */ - PluginRepository() {} - - /** - * Constructs ... - * - * - * @param id - * @param url - */ - public PluginRepository(String id, String url) - { - this.id = id; - this.url = url; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param obj - * - * @return - */ - @Override - public boolean equals(Object obj) - { - if (obj == null) - { - return false; - } - - if (getClass() != obj.getClass()) - { - return false; - } - - final PluginRepository other = (PluginRepository) obj; - - return Objects.equal(id, other.id) && Objects.equal(url, other.url); - } - - /** - * Method description - * - * - * @return - */ - @Override - public int hashCode() - { - return Objects.hashCode(id, url); - } - - /** - * Method description - * - * - * @return - */ - @Override - public String toString() - { - return MoreObjects.toStringHelper(this).add("id", id).add("url", - url).toString(); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public String getId() - { - return id; - } - - /** - * Method description - * - * - * @return - */ - public String getUrl() - { - return url; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String id; - - /** Field description */ - private String url; -} diff --git a/scm-core/src/main/java/sonia/scm/plugin/StatePluginPredicate.java b/scm-core/src/main/java/sonia/scm/plugin/StatePluginPredicate.java deleted file mode 100644 index ef7836f74a..0000000000 --- a/scm-core/src/main/java/sonia/scm/plugin/StatePluginPredicate.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.plugin; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.base.Predicate; - -/** - * - * @author Sebastian Sdorra - */ -public class StatePluginPredicate implements Predicate -{ - - /** - * Constructs ... - * - * - * @param state - */ - public StatePluginPredicate(PluginState state) - { - this.state = state; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param plugin - * - * @return - */ - @Override - public boolean apply(PluginInformation plugin) - { - return state == plugin.getState(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final PluginState state; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index 7ab49306fd..6e90106096 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -3,11 +3,10 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.plugin.AvailablePlugin; import sonia.scm.plugin.InstalledPluginDescriptor; -import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; -import sonia.scm.plugin.PluginState; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -18,9 +17,8 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; -import java.util.Collection; +import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; @@ -53,11 +51,8 @@ public class AvailablePluginResource { @Produces(VndMediaType.PLUGIN_COLLECTION) public Response getAvailablePlugins() { PluginPermissions.read().check(); - Collection plugins = pluginManager.getAvailable() - .stream() - .filter(plugin -> plugin.getState().equals(PluginState.AVAILABLE)) - .collect(Collectors.toList()); - return Response.ok(collectionMapper.map(plugins)).build(); + List available = pluginManager.getAvailable(); + return Response.ok(collectionMapper.mapAvailable(available)).build(); } /** @@ -66,7 +61,7 @@ public class AvailablePluginResource { * @return available plugin. */ @GET - @Path("/{name}/{version}") + @Path("/{name}") @StatusCodes({ @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 404, condition = "not found"), @@ -74,12 +69,9 @@ public class AvailablePluginResource { }) @TypeHint(PluginDto.class) @Produces(VndMediaType.PLUGIN) - public Response getAvailablePlugin(@PathParam("name") String name, @PathParam("version") String version) { + public Response getAvailablePlugin(@PathParam("name") String name) { PluginPermissions.read().check(); - Optional plugin = pluginManager.getAvailable() - .stream() - .filter(p -> p.getId().equals(name + ":" + version)) - .findFirst(); + Optional plugin = pluginManager.getAvailable(name); if (plugin.isPresent()) { return Response.ok(mapper.map(plugin.get())).build(); } else { @@ -90,19 +82,18 @@ public class AvailablePluginResource { /** * Triggers plugin installation. * @param name plugin artefact name - * @param version plugin version * @return HTTP Status. */ @POST - @Path("/{name}/{version}/install") + @Path("/{name}/install") @Consumes(VndMediaType.PLUGIN) @StatusCodes({ @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 500, condition = "internal server error") }) - public Response installPlugin(@PathParam("name") String name, @PathParam("version") String version) { + public Response installPlugin(@PathParam("name") String name) { PluginPermissions.manage().check(); - pluginManager.install(name + ":" + version); + pluginManager.install(name); return Response.ok().build(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java index 66347814a3..7c3f972a7b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java @@ -3,11 +3,10 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.plugin.InstalledPluginDescriptor; -import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; -import sonia.scm.plugin.InstalledPlugin; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -16,7 +15,6 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; -import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -25,17 +23,15 @@ import static sonia.scm.NotFoundException.notFound; public class InstalledPluginResource { - private final PluginLoader pluginLoader; private final PluginDtoCollectionMapper collectionMapper; private final PluginDtoMapper mapper; private final PluginManager pluginManager; @Inject - public InstalledPluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginManager pluginManager) { - this.pluginLoader = pluginLoader; + public InstalledPluginResource(PluginManager pluginManager, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper) { + this.pluginManager = pluginManager; this.collectionMapper = collectionMapper; this.mapper = mapper; - this.pluginManager = pluginManager; } /** @@ -53,8 +49,8 @@ public class InstalledPluginResource { @Produces(VndMediaType.PLUGIN_COLLECTION) public Response getInstalledPlugins() { PluginPermissions.read().check(); - List plugins = new ArrayList<>(pluginLoader.getInstalledPlugins()); - return Response.ok(collectionMapper.map(plugins)).build(); + List plugins = pluginManager.getInstalled(); + return Response.ok(collectionMapper.mapInstalled(plugins)).build(); } /** @@ -75,13 +71,9 @@ public class InstalledPluginResource { @Produces(VndMediaType.PLUGIN) public Response getInstalledPlugin(@PathParam("name") String name) { PluginPermissions.read().check(); - Optional pluginDto = pluginLoader.getInstalledPlugins() - .stream() - .filter(plugin -> name.equals(plugin.getDescriptor().getInformation().getName())) - .map(mapper::map) - .findFirst(); + Optional pluginDto = pluginManager.getInstalled(name); if (pluginDto.isPresent()) { - return Response.ok(pluginDto.get()).build(); + return Response.ok(mapper.map(pluginDto.get())).build(); } else { throw notFound(entity(InstalledPluginDescriptor.class, name)); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java index c835362df7..276eddfab6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java @@ -4,6 +4,7 @@ import com.google.inject.Inject; import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; +import sonia.scm.plugin.AvailablePlugin; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.InstalledPlugin; @@ -25,12 +26,12 @@ public class PluginDtoCollectionMapper { this.mapper = mapper; } - public HalRepresentation map(List plugins) { + public HalRepresentation mapInstalled(List plugins) { List dtos = plugins.stream().map(mapper::map).collect(toList()); return new HalRepresentation(createInstalledPluginsLinks(), embedDtos(dtos)); } - public HalRepresentation map(Collection plugins) { + public HalRepresentation mapAvailable(List plugins) { List dtos = plugins.stream().map(mapper::map).collect(toList()); return new HalRepresentation(createAvailablePluginsLinks(), embedDtos(dtos)); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index 4710fa943c..bef5a9b496 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -1,13 +1,11 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; -import org.mapstruct.ObjectFactory; +import sonia.scm.plugin.Plugin; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginState; -import sonia.scm.plugin.InstalledPlugin; import javax.inject.Inject; @@ -20,23 +18,23 @@ public abstract class PluginDtoMapper { @Inject private ResourceLinks resourceLinks; - public PluginDto map(InstalledPlugin plugin) { - return map(plugin.getDescriptor().getInformation()); - } + public abstract void map(PluginInformation plugin, @MappingTarget PluginDto dto); - public abstract PluginDto map(PluginInformation plugin); - - @AfterMapping - protected void appendCategory(@MappingTarget PluginDto dto) { + public PluginDto map(Plugin plugin) { + PluginDto dto = createDto(plugin); + map(plugin.getDescriptor().getInformation(), dto); if (dto.getCategory() == null) { dto.setCategory("Miscellaneous"); } + return dto; } - @ObjectFactory - public PluginDto createDto(PluginInformation pluginInformation) { + private PluginDto createDto(Plugin plugin) { Links.Builder linksBuilder; - if (pluginInformation.getState() != null && pluginInformation.getState().equals(PluginState.AVAILABLE)) { + + PluginInformation pluginInformation = plugin.getDescriptor().getInformation(); + + if (plugin.getState() != null && plugin.getState().equals(PluginState.AVAILABLE)) { linksBuilder = linkingTo() .self(resourceLinks.availablePlugin() .self(pluginInformation.getName(), pluginInformation.getVersion())); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index c19c19b377..4e91f5123b 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -35,685 +35,41 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- -import com.github.legman.Subscribe; - -import com.google.common.base.Predicate; -import com.google.common.io.Files; -import com.google.inject.Inject; import com.google.inject.Singleton; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sonia.scm.SCMContextProvider; -import sonia.scm.cache.Cache; -import sonia.scm.cache.CacheManager; -import sonia.scm.config.ScmConfiguration; -import sonia.scm.config.ScmConfigurationChangedEvent; -import sonia.scm.io.ZipUnArchiver; -import sonia.scm.util.AssertUtil; -import sonia.scm.util.IOUtil; -import sonia.scm.util.SystemUtil; -import sonia.scm.util.Util; -import sonia.scm.version.Version; - //~--- JDK imports ------------------------------------------------------------ - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; - -import java.net.URLEncoder; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import javax.xml.bind.JAXB; - -import sonia.scm.net.ahc.AdvancedHttpClient; - -import static sonia.scm.plugin.PluginCenterDtoMapper.*; +import java.util.List; +import java.util.Optional; /** - * TODO replace aether stuff. - * TODO check AdvancedPluginConfiguration from 1.x * * @author Sebastian Sdorra */ @Singleton -public class DefaultPluginManager implements PluginManager -{ +public class DefaultPluginManager implements PluginManager { - /** Field description */ - public static final String CACHE_NAME = "sonia.cache.plugins"; - - /** Field description */ - public static final String ENCODING = "UTF-8"; - - /** the logger for DefaultPluginManager */ - private static final Logger logger = - LoggerFactory.getLogger(DefaultPluginManager.class); - - /** enable or disable remote plugins */ - private static final boolean REMOTE_PLUGINS_ENABLED = true; - - /** Field description */ - public static final Predicate FILTER_UPDATES = - new StatePluginPredicate(PluginState.UPDATE_AVAILABLE); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * @param context - * @param configuration - * @param pluginLoader - * @param cacheManager - * @param httpClient - */ - @Inject - public DefaultPluginManager(SCMContextProvider context, - ScmConfiguration configuration, PluginLoader pluginLoader, - CacheManager cacheManager, AdvancedHttpClient httpClient) - { - this.context = context; - this.configuration = configuration; - this.cache = cacheManager.getCache(CACHE_NAME); - this.httpClient = httpClient; - installedPlugins = new HashMap<>(); - - for (InstalledPlugin wrapper : pluginLoader.getInstalledPlugins()) - { - InstalledPluginDescriptor plugin = wrapper.getDescriptor(); - PluginInformation info = plugin.getInformation(); - - if ((info != null) && info.isValid()) - { - installedPlugins.put(info.getId(), plugin); - } - } - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - */ @Override - public void clearCache() - { - if (logger.isDebugEnabled()) - { - logger.debug("clear plugin cache"); - } - - cache.clear(); + public Optional getAvailable(String name) { + return Optional.empty(); } - /** - * Method description - * - * - * @param config - */ - @Subscribe - public void configChanged(ScmConfigurationChangedEvent config) - { - clearCache(); - } - - /** - * Method description - * - * - * @param id - */ @Override - public void install(String id) - { - PluginPermissions.manage().check(); - - PluginCenter center = getPluginCenter(); - - for (PluginInformation plugin : center.getPlugins()) - { - String pluginId = plugin.getId(); - - if (Util.isNotEmpty(pluginId) && pluginId.equals(id)) - { - plugin.setState(PluginState.INSTALLED); - - // ugly workaround - InstalledPluginDescriptor newPlugin = new InstalledPluginDescriptor(); - - // TODO check - // newPlugin.setInformation(plugin); - installedPlugins.put(id, newPlugin); - } - } + public Optional getInstalled(String name) { + return Optional.empty(); } - /** - * Method description - * - * - * @param packageStream - * - * @throws IOException - */ @Override - public void installPackage(InputStream packageStream) throws IOException - { - PluginPermissions.manage().check(); - - File tempDirectory = Files.createTempDir(); - - try - { - new ZipUnArchiver().extractArchive(packageStream, tempDirectory); - - InstalledPluginDescriptor plugin = JAXB.unmarshal(new File(tempDirectory, "plugin.xml"), - InstalledPluginDescriptor.class); - - PluginCondition condition = plugin.getCondition(); - - if ((condition != null) &&!condition.isSupported()) - { - throw new PluginConditionFailedException(condition); - } - - /* - * AetherPluginHandler aph = new AetherPluginHandler(this, context, - * configuration); - * Collection repositories = - * Sets.newHashSet(new PluginRepository("package-repository", - * "file://".concat(tempDirectory.getAbsolutePath()))); - * - * aph.setPluginRepositories(repositories); - * - * aph.install(plugin.getInformation().getId()); - */ - plugin.getInformation().setState(PluginState.INSTALLED); - installedPlugins.put(plugin.getInformation().getId(), plugin); - - } - finally - { - IOUtil.delete(tempDirectory); - } + public List getInstalled() { + return null; } - /** - * Method description - * - * - * @param id - */ @Override - public void uninstall(String id) - { - PluginPermissions.manage().check(); - - InstalledPluginDescriptor plugin = installedPlugins.get(id); - - if (plugin == null) - { - String pluginPrefix = getPluginIdPrefix(id); - - for (String nid : installedPlugins.keySet()) - { - if (nid.startsWith(pluginPrefix)) - { - id = nid; - plugin = installedPlugins.get(nid); - - break; - } - } - } - - if (plugin == null) - { - throw new PluginNotInstalledException(id.concat(" is not install")); - } - - /* - * if (pluginHandler == null) - * { - * getPluginCenter(); - * } - * - * pluginHandler.uninstall(id); - */ - installedPlugins.remove(id); - preparePlugins(getPluginCenter()); + public List getAvailable() { + return null; } - /** - * Method description - * - * - * @param id - */ @Override - public void update(String id) - { - PluginPermissions.manage().check(); + public void install(String name) { - String[] idParts = id.split(":"); - String name = idParts[0]; - PluginInformation installed = null; - - for (PluginInformation info : getInstalled()) - { - if (name.equals(info.getName())) - { - installed = info; - - break; - } - } - - if (installed == null) - { - StringBuilder msg = new StringBuilder(name); - - msg.append(" is not install"); - - throw new PluginNotInstalledException(msg.toString()); - } - - uninstall(installed.getId()); - install(id); } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param id - * - * @return - */ - @Override - public PluginInformation get(String id) - { - PluginPermissions.read().check(); - - PluginInformation result = null; - - for (PluginInformation info : getPluginCenter().getPlugins()) - { - if (id.equals(info.getId())) - { - result = info; - - break; - } - } - - return result; - } - - /** - * Method description - * - * - * @param predicate - * - * @return - */ - @Override - public Set get(Predicate predicate) - { - AssertUtil.assertIsNotNull(predicate); - PluginPermissions.read().check(); - - Set infoSet = new HashSet<>(); - - filter(infoSet, getInstalled(), predicate); - filter(infoSet, getPluginCenter().getPlugins(), predicate); - - return infoSet; - } - - /** - * Method description - * - * - * @return - */ - @Override - public Collection getAll() - { - PluginPermissions.read().check(); - - Set infoSet = getInstalled(); - - infoSet.addAll(getPluginCenter().getPlugins()); - - return infoSet; - } - - /** - * Method description - * - * - * @return - */ - @Override - public Collection getAvailable() - { - PluginPermissions.read().check(); - - Set availablePlugins = new HashSet<>(); - Set centerPlugins = getPluginCenter().getPlugins(); - - for (PluginInformation info : centerPlugins) - { - if (!installedPlugins.containsKey(info.getName())) - { - availablePlugins.add(info); - } - } - - return availablePlugins; - } - - /** - * Method description - * - * - * @return - */ - @Override - public Set getAvailableUpdates() - { - PluginPermissions.read().check(); - - return get(FILTER_UPDATES); - } - - /** - * Method description - * - * - * @return - */ - @Override - public Set getInstalled() - { - PluginPermissions.read().check(); - - Set infoSet = new LinkedHashSet<>(); - - for (InstalledPluginDescriptor plugin : installedPlugins.values()) - { - infoSet.add(plugin.getInformation()); - } - - return infoSet; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * - * @param url - * @return - */ - private String buildPluginUrl(String url) - { - String os = SystemUtil.getOS(); - String arch = SystemUtil.getArch(); - - try - { - os = URLEncoder.encode(os, ENCODING); - } - catch (UnsupportedEncodingException ex) - { - logger.error(ex.getMessage(), ex); - } - - return url.replace("{version}", context.getVersion()).replace("{os}", - os).replace("{arch}", arch); - } - - /** - * Method description - * - * - * @param target - * @param source - * @param predicate - */ - private void filter(Set target, - Collection source, - Predicate predicate) - { - for (PluginInformation info : source) - { - if (predicate.apply(info)) - { - target.add(info); - } - } - } - - /** - * Method description - * - * - * @param available - */ - private void preparePlugin(PluginInformation available) - { - PluginState state = PluginState.AVAILABLE; - - for (PluginInformation installed : getInstalled()) - { - if (isSamePlugin(available, installed)) - { - if (installed.getVersion().equals(available.getVersion())) - { - state = PluginState.INSTALLED; - } - else if (isNewer(available, installed)) - { - state = PluginState.UPDATE_AVAILABLE; - } - else - { - state = PluginState.NEWER_VERSION_INSTALLED; - } - - break; - } - } - - available.setState(state); - } - - /** - * Method description - * - * - * @param pc - */ - private void preparePlugins(PluginCenter pc) - { - Set infoSet = pc.getPlugins(); - - if (infoSet != null) - { - Iterator pit = infoSet.iterator(); - - while (pit.hasNext()) - { - PluginInformation available = pit.next(); - - if (isCorePluging(available)) - { - pit.remove(); - } - else - { - preparePlugin(available); - } - } - } - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - private PluginCenter getPluginCenter() - { - PluginCenter center = cache.get(PluginCenter.class.getName()); - - if (center == null) - { - synchronized (DefaultPluginManager.class) - { - String pluginUrl = buildPluginUrl(configuration.getPluginUrl()); - logger.info("fetch plugin information from {}", pluginUrl); - - if (REMOTE_PLUGINS_ENABLED && Util.isNotEmpty(pluginUrl)) - { - try - { - center = new PluginCenter(); - PluginCenterDto pluginCenterDto = httpClient.get(pluginUrl).request().contentFromJson(PluginCenterDto.class); - Set pluginInformationSet = map(pluginCenterDto.getEmbedded().getPlugins()); - center.setPlugins(pluginInformationSet); - preparePlugins(center); - cache.put(PluginCenter.class.getName(), center); - } - catch (IOException ex) - { - logger.error("could not load plugins from plugin center", ex); - } - } - } - if(center == null) { - center = new PluginCenter(); - } - } - - return center; - } - - /** - * Method description - * - * - * @param pluginId - * - * @return - */ - private String getPluginIdPrefix(String pluginId) - { - return pluginId.substring(0, pluginId.lastIndexOf(':')); - } - - /** - * Method description - * - * - * @param available - * - * @return - */ - private boolean isCorePluging(PluginInformation available) - { - boolean core = false; - - for (InstalledPluginDescriptor installedPlugin : installedPlugins.values()) - { - PluginInformation installed = installedPlugin.getInformation(); - - if (isSamePlugin(available, installed) - && (installed.getState() == PluginState.CORE)) - { - core = true; - - break; - } - } - - return core; - } - - /** - * Method description - * - * - * @param available - * @param installed - * - * @return - */ - private boolean isNewer(PluginInformation available, - PluginInformation installed) - { - boolean result = false; - Version version = Version.parse(available.getVersion()); - - if (version != null) - { - result = version.isNewer(installed.getVersion()); - } - - return result; - } - - /** - * Method description - * - * - * @param p1 - * @param p2 - * - * @return - */ - private boolean isSamePlugin(PluginInformation p1, PluginInformation p2) - { - return p1.getName().equals(p2.getName()); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final Cache cache; - - /** Field description */ - private final AdvancedHttpClient httpClient; - - /** Field description */ - private final ScmConfiguration configuration; - - /** Field description */ - private final SCMContextProvider context; - - /** Field description */ - private final Map installedPlugins; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/OverviewPluginPredicate.java b/scm-webapp/src/main/java/sonia/scm/plugin/OverviewPluginPredicate.java deleted file mode 100644 index b242813e0d..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/plugin/OverviewPluginPredicate.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.plugin; - -import com.google.common.base.Predicate; - -/** - * - * @author Sebastian Sdorra - */ -public class OverviewPluginPredicate implements Predicate -{ - - /** Field description */ - public static final OverviewPluginPredicate INSTANCE = - new OverviewPluginPredicate(); - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param plugin - * - * @return - */ - @Override - public boolean apply(PluginInformation plugin) - { - return plugin.getState() != PluginState.NEWER_VERSION_INSTALLED; - } -} 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 ed1bc7643a..5ae4a32897 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -237,7 +237,7 @@ public final class PluginProcessor } InstalledPlugin plugin = - createPluginWrapper(createParentPluginClassLoader(classLoader, parents), + createPlugin(createParentPluginClassLoader(classLoader, parents), smp); if (plugin != null) @@ -431,73 +431,36 @@ public final class PluginProcessor return result; } - /** - * Method description - * - * - * - * @param classLoader - * @param descriptor - * - * @return - */ - private InstalledPluginDescriptor createPlugin(ClassLoader classLoader, Path descriptor) - { + private InstalledPluginDescriptor createDescriptor(ClassLoader classLoader, Path descriptor) { ClassLoader ctxcl = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader); - - try - { - return (InstalledPluginDescriptor) context.createUnmarshaller().unmarshal( - descriptor.toFile()); - } - catch (JAXBException ex) - { - throw new PluginLoadException( - "could not load plugin desriptor ".concat(descriptor.toString()), ex); - } - finally - { + try { + return (InstalledPluginDescriptor) context.createUnmarshaller().unmarshal(descriptor.toFile()); + } catch (JAXBException ex) { + throw new PluginLoadException("could not load plugin desriptor ".concat(descriptor.toString()), ex); + } finally { Thread.currentThread().setContextClassLoader(ctxcl); } } - /** - * Method description - * - * - * @param classLoader - * @param smp - * - * @return - * - * @throws IOException - */ - private InstalledPlugin createPluginWrapper(ClassLoader classLoader, - ExplodedSmp smp) - throws IOException - { - InstalledPlugin wrapper = null; + private InstalledPlugin createPlugin(ClassLoader classLoader, ExplodedSmp smp) throws IOException { + InstalledPlugin plugin = null; Path directory = smp.getPath(); - Path descriptor = directory.resolve(PluginConstants.FILE_DESCRIPTOR); + Path descriptorPath = directory.resolve(PluginConstants.FILE_DESCRIPTOR); - if (Files.exists(descriptor)) - { + if (Files.exists(descriptorPath)) { ClassLoader cl = createClassLoader(classLoader, smp); - InstalledPluginDescriptor plugin = createPlugin(cl, descriptor); + InstalledPluginDescriptor descriptor = createDescriptor(cl, descriptorPath); WebResourceLoader resourceLoader = createWebResourceLoader(directory); - wrapper = new InstalledPlugin(plugin, cl, resourceLoader, directory); - } - else - { + plugin = new InstalledPlugin(descriptor, cl, resourceLoader, directory); + } else { logger.warn("found plugin directory without plugin descriptor"); } - return wrapper; + return plugin; } /** diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java index 57564999ef..54954c19d7 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java @@ -16,6 +16,9 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.plugin.AvailablePlugin; +import sonia.scm.plugin.AvailablePluginDescriptor; +import sonia.scm.plugin.PluginCondition; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginState; @@ -27,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.util.Collections; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -87,10 +91,10 @@ class AvailablePluginResourceTest { @Test void getAvailablePlugins() throws URISyntaxException, UnsupportedEncodingException { - PluginInformation pluginInformation = new PluginInformation(); - pluginInformation.setState(PluginState.AVAILABLE); - when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(pluginInformation)); - when(collectionMapper.map(Collections.singletonList(pluginInformation))).thenReturn(new MockedResultDto()); + AvailablePlugin plugin = createPlugin(); + + when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(plugin)); + when(collectionMapper.mapAvailable(Collections.singletonList(plugin))).thenReturn(new MockedResultDto()); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available"); request.accept(VndMediaType.PLUGIN_COLLECTION); @@ -105,16 +109,18 @@ class AvailablePluginResourceTest { @Test void getAvailablePlugin() throws UnsupportedEncodingException, URISyntaxException { PluginInformation pluginInformation = new PluginInformation(); - pluginInformation.setState(PluginState.AVAILABLE); pluginInformation.setName("pluginName"); pluginInformation.setVersion("2.0.0"); - when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(pluginInformation)); + + AvailablePlugin plugin = createPlugin(pluginInformation); + + when(pluginManager.getAvailable("pluginName")).thenReturn(Optional.of(plugin)); PluginDto pluginDto = new PluginDto(); pluginDto.setName("pluginName"); - when(mapper.map(pluginInformation)).thenReturn(pluginDto); + when(mapper.map(plugin)).thenReturn(pluginDto); - MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName/2.0.0"); + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName"); request.accept(VndMediaType.PLUGIN); MockHttpResponse response = new MockHttpResponse(); @@ -126,17 +132,26 @@ class AvailablePluginResourceTest { @Test void installPlugin() throws URISyntaxException { - MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/2.0.0/install"); + MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/install"); request.accept(VndMediaType.PLUGIN); MockHttpResponse response = new MockHttpResponse(); dispatcher.invoke(request, response); - verify(pluginManager).install("pluginName:2.0.0"); + verify(pluginManager).install("pluginName"); assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus()); } } + private AvailablePlugin createPlugin() { + return createPlugin(new PluginInformation()); + } + + private AvailablePlugin createPlugin(PluginInformation pluginInformation) { + AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor(pluginInformation, new PluginCondition(), Collections.emptySet()); + return new AvailablePlugin(descriptor); + } + @Nested class WithoutAuthorization { @@ -156,7 +171,7 @@ class AvailablePluginResourceTest { @Test void shouldNotGetAvailablePluginIfMissingPermission() throws URISyntaxException { - MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName/2.0.0"); + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName"); request.accept(VndMediaType.PLUGIN); MockHttpResponse response = new MockHttpResponse(); @@ -166,7 +181,7 @@ class AvailablePluginResourceTest { @Test void shouldNotInstallPluginIfMissingPermission() throws URISyntaxException { ThreadContext.unbindSubject(); - MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/2.0.0/install"); + MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/install"); request.accept(VndMediaType.PLUGIN); MockHttpResponse response = new MockHttpResponse(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index ce781c7c32..c5868a1211 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -16,11 +16,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.plugin.InstalledPluginDescriptor; import sonia.scm.plugin.PluginInformation; -import sonia.scm.plugin.PluginLoader; -import sonia.scm.plugin.PluginState; -import sonia.scm.plugin.InstalledPlugin; +import sonia.scm.plugin.PluginManager; import sonia.scm.web.VndMediaType; import javax.inject.Provider; @@ -28,12 +27,12 @@ import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.util.Collections; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class InstalledPluginResourceTest { @@ -46,15 +45,15 @@ class InstalledPluginResourceTest { @Mock Provider availablePluginResourceProvider; - @Mock - private PluginLoader pluginLoader; - @Mock private PluginDtoCollectionMapper collectionMapper; @Mock private PluginDtoMapper mapper; + @Mock + private PluginManager pluginManager; + @InjectMocks InstalledPluginResource installedPluginResource; @@ -86,9 +85,9 @@ class InstalledPluginResourceTest { @Test void getInstalledPlugins() throws URISyntaxException, UnsupportedEncodingException { - InstalledPlugin installedPlugin = new InstalledPlugin(null, null, null, null); - when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(installedPlugin)); - when(collectionMapper.map(Collections.singletonList(installedPlugin))).thenReturn(new MockedResultDto()); + InstalledPlugin installedPlugin = createPlugin(); + when(pluginManager.getInstalled()).thenReturn(Collections.singletonList(installedPlugin)); + when(collectionMapper.mapInstalled(Collections.singletonList(installedPlugin))).thenReturn(new MockedResultDto()); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); request.accept(VndMediaType.PLUGIN_COLLECTION); @@ -105,10 +104,9 @@ class InstalledPluginResourceTest { PluginInformation pluginInformation = new PluginInformation(); pluginInformation.setVersion("2.0.0"); pluginInformation.setName("pluginName"); - pluginInformation.setState(PluginState.INSTALLED); - InstalledPluginDescriptor plugin = new InstalledPluginDescriptor(2, pluginInformation, null, null, false, null); - InstalledPlugin installedPlugin = new InstalledPlugin(plugin, null, null, null); - when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(installedPlugin)); + InstalledPlugin installedPlugin = createPlugin(pluginInformation); + + when(pluginManager.getInstalled("pluginName")).thenReturn(Optional.of(installedPlugin)); PluginDto pluginDto = new PluginDto(); pluginDto.setName("pluginName"); @@ -125,6 +123,18 @@ class InstalledPluginResourceTest { } } + private InstalledPlugin createPlugin() { + return createPlugin(new PluginInformation()); + } + + private InstalledPlugin createPlugin(PluginInformation information) { + InstalledPlugin plugin = mock(InstalledPlugin.class); + InstalledPluginDescriptor descriptor = mock(InstalledPluginDescriptor.class); + lenient().when(descriptor.getInformation()).thenReturn(information); + lenient().when(plugin.getDescriptor()).thenReturn(descriptor); + return plugin; + } + @Nested class WithoutAuthorization { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java index 97b46603d3..df3b8e101b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java @@ -4,13 +4,19 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.plugin.Plugin; +import sonia.scm.plugin.PluginDescriptor; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginState; import java.net.URI; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.in; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class PluginDtoMapperTest { @@ -25,7 +31,8 @@ class PluginDtoMapperTest { void shouldMapInformation() { PluginInformation information = createPluginInformation(); - PluginDto dto = mapper.map(information); + PluginDto dto = new PluginDto(); + mapper.map(information, dto); assertThat(dto.getName()).isEqualTo("scm-cas-plugin"); assertThat(dto.getVersion()).isEqualTo("1.0.0"); @@ -48,41 +55,51 @@ class PluginDtoMapperTest { @Test void shouldAppendInstalledSelfLink() { - PluginInformation information = createPluginInformation(); - information.setState(PluginState.INSTALLED); + Plugin plugin = createPlugin(PluginState.INSTALLED); - PluginDto dto = mapper.map(information); + PluginDto dto = mapper.map(plugin); assertThat(dto.getLinks().getLinkBy("self").get().getHref()) .isEqualTo("https://hitchhiker.com/v2/plugins/installed/scm-cas-plugin"); } @Test void shouldAppendAvailableSelfLink() { - PluginInformation information = createPluginInformation(); - information.setState(PluginState.AVAILABLE); + Plugin plugin = createPlugin(PluginState.AVAILABLE); - PluginDto dto = mapper.map(information); + PluginDto dto = mapper.map(plugin); assertThat(dto.getLinks().getLinkBy("self").get().getHref()) - .isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/1.0.0"); + .isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin"); } @Test void shouldAppendInstallLink() { - PluginInformation information = createPluginInformation(); - information.setState(PluginState.AVAILABLE); + Plugin plugin = createPlugin(PluginState.AVAILABLE); - PluginDto dto = mapper.map(information); + PluginDto dto = mapper.map(plugin); assertThat(dto.getLinks().getLinkBy("install").get().getHref()) - .isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/1.0.0/install"); + .isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/install"); } @Test void shouldReturnMiscellaneousIfCategoryIsNull() { PluginInformation information = createPluginInformation(); information.setCategory(null); - - PluginDto dto = mapper.map(information); + Plugin plugin = createPlugin(information, PluginState.AVAILABLE); + PluginDto dto = mapper.map(plugin); assertThat(dto.getCategory()).isEqualTo("Miscellaneous"); } + private Plugin createPlugin(PluginState state) { + return createPlugin(createPluginInformation(), state); + } + + private Plugin createPlugin(PluginInformation information, PluginState state) { + Plugin plugin = Mockito.mock(Plugin.class); + when(plugin.getState()).thenReturn(state); + PluginDescriptor descriptor = mock(PluginDescriptor.class); + when(descriptor.getInformation()).thenReturn(information); + when(plugin.getDescriptor()).thenReturn(descriptor); + return plugin; + } + } From 9d66f146277ce401734dc5b81a5139faf1563b45 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 12:29:59 +0200 Subject: [PATCH 06/27] implement simplified PluginManager API --- .../sonia/scm/plugin/PluginInformation.java | 4 - .../scm/plugin/DefaultPluginManager.java | 36 ++++- .../java/sonia/scm/plugin/PluginCenter.java | 55 +++++++ .../scm/plugin/PluginCenterDtoMapper.java | 22 +-- .../sonia/scm/plugin/PluginCenterLoader.java | 42 +++++ .../scm/plugin/DefaultPluginManagerTest.java | 149 ++++++++++++++++++ .../scm/plugin/PluginCenterDtoMapperTest.java | 44 ++++-- .../scm/plugin/PluginCenterLoaderTest.java | 50 ++++++ .../sonia/scm/plugin/PluginCenterTest.java | 73 +++++++++ 9 files changed, 443 insertions(+), 32 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginCenter.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterTest.java diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index a9ae3b07f6..b669cb63fa 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -70,7 +70,6 @@ public class PluginInformation implements PermissionObject, Validateable, Clonea private String author; private String category; private String avatarUrl; - private PluginCondition condition; @Override public PluginInformation clone() { @@ -82,9 +81,6 @@ public class PluginInformation implements PermissionObject, Validateable, Clonea clone.setAuthor(author); clone.setCategory(category); clone.setAvatarUrl(avatarUrl); - if (condition != null) { - clone.setCondition(condition.clone()); - } return clone; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 4e91f5123b..e465c8306e 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -35,11 +35,15 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.collect.ImmutableList; import com.google.inject.Singleton; //~--- JDK imports ------------------------------------------------------------ +import javax.inject.Inject; import java.util.List; import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** * @@ -48,24 +52,48 @@ import java.util.Optional; @Singleton public class DefaultPluginManager implements PluginManager { + private final PluginLoader loader; + private final PluginCenter center; + + @Inject + public DefaultPluginManager(PluginLoader loader, PluginCenter center) { + this.loader = loader; + this.center = center; + } + @Override public Optional getAvailable(String name) { - return Optional.empty(); + return center.getAvailable() + .stream() + .filter(filterByName(name)) + .filter(this::isNotInstalled) + .findFirst(); } @Override public Optional getInstalled(String name) { - return Optional.empty(); + return loader.getInstalledPlugins() + .stream() + .filter(filterByName(name)) + .findFirst(); } @Override public List getInstalled() { - return null; + return ImmutableList.copyOf(loader.getInstalledPlugins()); } @Override public List getAvailable() { - return null; + return center.getAvailable().stream().filter(this::isNotInstalled).collect(Collectors.toList()); + } + + private Predicate filterByName(String name) { + return (plugin) -> name.equals(plugin.getDescriptor().getInformation().getName()); + } + + private boolean isNotInstalled(AvailablePlugin availablePlugin) { + return !getInstalled(availablePlugin.getDescriptor().getInformation().getName()).isPresent(); } @Override diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenter.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenter.java new file mode 100644 index 0000000000..a3817eba0f --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenter.java @@ -0,0 +1,55 @@ +package sonia.scm.plugin; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.SCMContextProvider; +import sonia.scm.cache.Cache; +import sonia.scm.cache.CacheManager; +import sonia.scm.config.ScmConfiguration; +import sonia.scm.util.HttpUtil; +import sonia.scm.util.SystemUtil; + +import javax.inject.Inject; +import java.util.Set; + +public class PluginCenter { + + private static final String CACHE_NAME = "sonia.cache.plugins"; + + private static final Logger LOG = LoggerFactory.getLogger(PluginCenter.class); + + private final SCMContextProvider context; + private final ScmConfiguration configuration; + private final PluginCenterLoader loader; + private final Cache> cache; + + @Inject + public PluginCenter(SCMContextProvider context, CacheManager cacheManager, ScmConfiguration configuration, PluginCenterLoader loader) { + this.context = context; + this.configuration = configuration; + this.loader = loader; + this.cache = cacheManager.getCache(CACHE_NAME); + } + + synchronized Set getAvailable() { + String url = buildPluginUrl(configuration.getPluginUrl()); + Set plugins = cache.get(url); + if (plugins == null) { + LOG.debug("no cached available plugins found, start fetching"); + plugins = loader.load(url); + cache.put(url, plugins); + } else { + LOG.debug("return available plugins from cache"); + } + return plugins; + } + + private String buildPluginUrl(String url) { + String os = HttpUtil.encode(SystemUtil.getOS()); + String arch = SystemUtil.getArch(); + return url.replace("{version}", context.getVersion()) + .replace("{os}", os) + .replace("{arch}", arch); + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java index ea445b3ede..972afa3099 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java @@ -1,26 +1,26 @@ package sonia.scm.plugin; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; import java.util.HashSet; -import java.util.List; import java.util.Set; @Mapper -public interface PluginCenterDtoMapper { +public abstract class PluginCenterDtoMapper { - @Mapping(source = "conditions", target = "condition") - PluginInformation map(PluginCenterDto.Plugin plugin); + static final PluginCenterDtoMapper INSTANCE = Mappers.getMapper(PluginCenterDtoMapper.class); - PluginCondition map(PluginCenterDto.Condition condition); + abstract PluginInformation map(PluginCenterDto.Plugin plugin); + abstract PluginCondition map(PluginCenterDto.Condition condition); - static Set map(List dtos) { - PluginCenterDtoMapper mapper = Mappers.getMapper(PluginCenterDtoMapper.class); - Set plugins = new HashSet<>(); - for (PluginCenterDto.Plugin plugin : dtos) { - plugins.add(mapper.map(plugin)); + Set map(PluginCenterDto pluginCenterDto) { + Set plugins = new HashSet<>(); + for (PluginCenterDto.Plugin plugin : pluginCenterDto.getEmbedded().getPlugins()) { + AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor( + map(plugin), map(plugin.getConditions()), plugin.getDependencies() + ); + plugins.add(new AvailablePlugin(descriptor)); } return plugins; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java new file mode 100644 index 0000000000..2b0928891e --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java @@ -0,0 +1,42 @@ +package sonia.scm.plugin; + +import com.google.common.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.net.ahc.AdvancedHttpClient; + +import javax.inject.Inject; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; + +class PluginCenterLoader { + + private static final Logger LOG = LoggerFactory.getLogger(PluginCenterLoader.class); + + private final AdvancedHttpClient client; + private final PluginCenterDtoMapper mapper; + + @Inject + public PluginCenterLoader(AdvancedHttpClient client) { + this(client, PluginCenterDtoMapper.INSTANCE); + } + + @VisibleForTesting + PluginCenterLoader(AdvancedHttpClient client, PluginCenterDtoMapper mapper) { + this.client = client; + this.mapper = mapper; + } + + Set load(String url) { + try { + LOG.info("fetch plugins from {}", url); + PluginCenterDto pluginCenterDto = client.get(url).request().contentFromJson(PluginCenterDto.class); + return mapper.map(pluginCenterDto); + } catch (IOException ex) { + LOG.error("failed to load plugins from plugin center, returning empty list"); + return Collections.emptySet(); + } + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java new file mode 100644 index 0000000000..22fe370915 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -0,0 +1,149 @@ +package sonia.scm.plugin; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.Opt; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class DefaultPluginManagerTest { + + @Mock + private PluginLoader loader; + + @Mock + private PluginCenter center; + + @InjectMocks + private DefaultPluginManager manager; + + @Test + void shouldReturnInstalledPlugins() { + InstalledPlugin review = createInstalled("scm-review-plugin"); + InstalledPlugin git = createInstalled("scm-git-plugin"); + + when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(review, git)); + + List installed = manager.getInstalled(); + assertThat(installed).containsOnly(review, git); + } + + @Test + void shouldReturnReviewPlugin() { + InstalledPlugin review = createInstalled("scm-review-plugin"); + InstalledPlugin git = createInstalled("scm-git-plugin"); + + when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(review, git)); + + Optional plugin = manager.getInstalled("scm-review-plugin"); + assertThat(plugin).contains(review); + } + + @Test + void shouldReturnEmptyForNonInstalledPlugin() { + when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of()); + + Optional plugin = manager.getInstalled("scm-review-plugin"); + assertThat(plugin).isEmpty(); + } + + @Test + void shouldReturnAvailablePlugins() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + AvailablePlugin git = createAvailable("scm-git-plugin"); + + when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git)); + + List available = manager.getAvailable(); + assertThat(available).containsOnly(review, git); + } + + @Test + void shouldFilterOutAllInstalled() { + InstalledPlugin installedGit = createInstalled("scm-git-plugin"); + when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedGit)); + + AvailablePlugin review = createAvailable("scm-review-plugin"); + AvailablePlugin git = createAvailable("scm-git-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git)); + + List available = manager.getAvailable(); + assertThat(available).containsOnly(review); + } + + @Test + void shouldReturnAvailable() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + AvailablePlugin git = createAvailable("scm-git-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git)); + + Optional available = manager.getAvailable("scm-git-plugin"); + assertThat(available).contains(git); + } + + @Test + void shouldReturnEmptyForNonExistingAvailable() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review)); + + Optional available = manager.getAvailable("scm-git-plugin"); + assertThat(available).isEmpty(); + } + + @Test + void shouldReturnEmptyForInstalledPlugin() { + InstalledPlugin installedGit = createInstalled("scm-git-plugin"); + when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedGit)); + + AvailablePlugin git = createAvailable("scm-git-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(git)); + + Optional available = manager.getAvailable("scm-git-plugin"); + assertThat(available).isEmpty(); + } + + private AvailablePlugin createAvailable(String name) { + PluginInformation information = new PluginInformation(); + information.setName(name); + return createAvailable(information); + } + + private InstalledPlugin createInstalled(String name) { + PluginInformation information = new PluginInformation(); + information.setName(name); + return createInstalled(information); + } + + private InstalledPlugin createInstalled(PluginInformation information) { + InstalledPlugin plugin = mock(InstalledPlugin.class, Answers.RETURNS_DEEP_STUBS); + returnInformation(plugin, information); + return plugin; + } + + private AvailablePlugin createAvailable(PluginInformation information) { + AvailablePlugin plugin = mock(AvailablePlugin.class, Answers.RETURNS_DEEP_STUBS); + returnInformation(plugin, information); + return plugin; + } + + private void returnInformation(Plugin mockedPlugin, PluginInformation information) { + when(mockedPlugin.getDescriptor().getInformation()).thenReturn(information); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java index 831e847843..0c0b80ccfa 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java @@ -2,6 +2,11 @@ package sonia.scm.plugin; import com.google.common.collect.ImmutableSet; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.Arrays; @@ -11,11 +16,19 @@ import java.util.List; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; import static sonia.scm.plugin.PluginCenterDto.Plugin; import static sonia.scm.plugin.PluginCenterDto.*; +@ExtendWith(MockitoExtension.class) class PluginCenterDtoMapperTest { + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private PluginCenterDto dto; + + @InjectMocks + private PluginCenterDtoMapperImpl mapper; + @Test void shouldMapSinglePlugin() { Plugin plugin = new Plugin( @@ -31,16 +44,19 @@ class PluginCenterDtoMapperTest { ImmutableSet.of("scm-review-plugin"), new HashMap<>()); - PluginInformation result = PluginCenterDtoMapper.map(Collections.singletonList(plugin)).iterator().next(); + when(dto.getEmbedded().getPlugins()).thenReturn(Collections.singletonList(plugin)); + AvailablePluginDescriptor descriptor = mapper.map(dto).iterator().next().getDescriptor(); + PluginInformation information = descriptor.getInformation(); + PluginCondition condition = descriptor.getCondition(); - assertThat(result.getAuthor()).isEqualTo(plugin.getAuthor()); - assertThat(result.getCategory()).isEqualTo(plugin.getCategory()); - assertThat(result.getVersion()).isEqualTo(plugin.getVersion()); - assertThat(result.getCondition().getArch()).isEqualTo(plugin.getConditions().getArch()); - assertThat(result.getCondition().getMinVersion()).isEqualTo(plugin.getConditions().getMinVersion()); - assertThat(result.getCondition().getOs().iterator().next()).isEqualTo(plugin.getConditions().getOs().iterator().next()); - assertThat(result.getDescription()).isEqualTo(plugin.getDescription()); - assertThat(result.getName()).isEqualTo(plugin.getName()); + assertThat(information.getAuthor()).isEqualTo(plugin.getAuthor()); + assertThat(information.getCategory()).isEqualTo(plugin.getCategory()); + assertThat(information.getVersion()).isEqualTo(plugin.getVersion()); + assertThat(condition.getArch()).isEqualTo(plugin.getConditions().getArch()); + assertThat(condition.getMinVersion()).isEqualTo(plugin.getConditions().getMinVersion()); + assertThat(condition.getOs().iterator().next()).isEqualTo(plugin.getConditions().getOs().iterator().next()); + assertThat(information.getDescription()).isEqualTo(plugin.getDescription()); + assertThat(information.getName()).isEqualTo(plugin.getName()); } @Test @@ -71,12 +87,14 @@ class PluginCenterDtoMapperTest { ImmutableSet.of("scm-review-plugin"), new HashMap<>()); - Set resultSet = PluginCenterDtoMapper.map(Arrays.asList(plugin1, plugin2)); + when(dto.getEmbedded().getPlugins()).thenReturn(Arrays.asList(plugin1, plugin2)); - List pluginsList = new ArrayList<>(resultSet); + Set resultSet = mapper.map(dto); - PluginInformation pluginInformation1 = pluginsList.get(1); - PluginInformation pluginInformation2 = pluginsList.get(0); + List pluginsList = new ArrayList<>(resultSet); + + PluginInformation pluginInformation1 = pluginsList.get(1).getDescriptor().getInformation(); + PluginInformation pluginInformation2 = pluginsList.get(0).getDescriptor().getInformation(); assertThat(pluginInformation1.getAuthor()).isEqualTo(plugin1.getAuthor()); assertThat(pluginInformation1.getVersion()).isEqualTo(plugin1.getVersion()); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java new file mode 100644 index 0000000000..e3ebf995bd --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java @@ -0,0 +1,50 @@ +package sonia.scm.plugin; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.net.ahc.AdvancedHttpClient; + +import java.io.IOException; +import java.util.Collections; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PluginCenterLoaderTest { + + private static final String PLUGIN_URL = "https://plugins.hitchhiker.com"; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private AdvancedHttpClient client; + + @Mock + private PluginCenterDtoMapper mapper; + + @InjectMocks + private PluginCenterLoader loader; + + @Test + void shouldFetch() throws IOException { + Set plugins = Collections.emptySet(); + PluginCenterDto dto = new PluginCenterDto(); + when(client.get(PLUGIN_URL).request().contentFromJson(PluginCenterDto.class)).thenReturn(dto); + when(mapper.map(dto)).thenReturn(plugins); + + Set fetched = loader.load(PLUGIN_URL); + assertThat(fetched).isSameAs(plugins); + } + + @Test + void shouldReturnEmptySetIfPluginCenterNotBeReached() throws IOException { + when(client.get(PLUGIN_URL).request()).thenThrow(new IOException("failed to fetch")); + + Set fetch = loader.load(PLUGIN_URL); + assertThat(fetch).isEmpty(); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterTest.java new file mode 100644 index 0000000000..a76b4cb551 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterTest.java @@ -0,0 +1,73 @@ +package sonia.scm.plugin; + +import com.google.common.collect.ImmutableSet; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.SCMContextProvider; +import sonia.scm.cache.CacheManager; +import sonia.scm.cache.MapCacheManager; +import sonia.scm.config.ScmConfiguration; +import sonia.scm.net.ahc.AdvancedHttpClient; +import sonia.scm.util.SystemUtil; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PluginCenterTest { + + private static final String PLUGIN_URL_BASE = "https://plugins.hitchhiker.com/"; + private static final String PLUGIN_URL = PLUGIN_URL_BASE + "{version}"; + + @Mock + private PluginCenterLoader loader; + + @Mock + private SCMContextProvider contextProvider; + + private ScmConfiguration configuration; + + private CacheManager cacheManager; + + private PluginCenter pluginCenter; + + @BeforeEach + void setUpPluginCenter() { + when(contextProvider.getVersion()).thenReturn("2.0.0"); + + cacheManager = new MapCacheManager(); + + configuration = new ScmConfiguration(); + configuration.setPluginUrl(PLUGIN_URL); + + pluginCenter = new PluginCenter(contextProvider, cacheManager, configuration, loader); + } + + @Test + void shouldFetchPlugins() { + Set plugins = new HashSet<>(); + when(loader.load(PLUGIN_URL_BASE + "2.0.0")).thenReturn(plugins); + + assertThat(pluginCenter.getAvailable()).isSameAs(plugins); + } + + @Test + void shouldCache() { + Set first = new HashSet<>(); + when(loader.load(anyString())).thenReturn(first, new HashSet<>()); + + assertThat(pluginCenter.getAvailable()).isSameAs(first); + assertThat(pluginCenter.getAvailable()).isSameAs(first); + } + +} From 65b59d1aec41f6931f44e9657f6ebc15b255f67c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 13:40:05 +0200 Subject: [PATCH 07/27] expose plugin dependencies --- .../java/sonia/scm/api/v2/resources/PluginDto.java | 3 +++ .../sonia/scm/api/v2/resources/PluginDtoMapper.java | 1 + .../scm/api/v2/resources/PluginDtoMapperTest.java | 12 ++++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java index b096266537..07ccb3203e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java @@ -6,6 +6,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.Set; + @Getter @Setter @NoArgsConstructor @@ -18,6 +20,7 @@ public class PluginDto extends HalRepresentation { private String author; private String category; private String avatarUrl; + private Set dependencies; public PluginDto(Links links) { add(links); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index bef5a9b496..193aa3af26 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -22,6 +22,7 @@ public abstract class PluginDtoMapper { public PluginDto map(Plugin plugin) { PluginDto dto = createDto(plugin); + dto.setDependencies(plugin.getDescriptor().getDependencies()); map(plugin.getDescriptor().getInformation(), dto); if (dto.getCategory() == null) { dto.setCategory("Miscellaneous"); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java index df3b8e101b..3eaeb7a2cc 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java @@ -1,6 +1,6 @@ package sonia.scm.api.v2.resources; -import org.junit.jupiter.api.BeforeEach; +import com.google.common.collect.ImmutableSet; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -14,7 +14,6 @@ import sonia.scm.plugin.PluginState; import java.net.URI; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.in; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -89,6 +88,15 @@ class PluginDtoMapperTest { assertThat(dto.getCategory()).isEqualTo("Miscellaneous"); } + @Test + void shouldAppendDependencies() { + Plugin plugin = createPlugin(PluginState.AVAILABLE); + when(plugin.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("one", "two")); + + PluginDto dto = mapper.map(plugin); + assertThat(dto.getDependencies()).containsOnly("one", "two"); + } + private Plugin createPlugin(PluginState state) { return createPlugin(createPluginInformation(), state); } From e24673be0a51c1b84921b706c62af7818a91d05d Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 14:43:48 +0200 Subject: [PATCH 08/27] implemented plugin installation --- .../scm/plugin/AvailablePluginDescriptor.java | 15 +++- .../scm/plugin/DefaultPluginManager.java | 24 ++++- .../sonia/scm/plugin/PluginCenterDto.java | 3 + .../scm/plugin/PluginCenterDtoMapper.java | 3 +- .../PluginChecksumMismatchException.java | 7 ++ .../scm/plugin/PluginDownloadException.java | 7 ++ .../scm/plugin/PluginInstallException.java | 12 +++ .../sonia/scm/plugin/PluginInstaller.java | 63 +++++++++++++ .../AvailablePluginResourceTest.java | 5 +- .../scm/plugin/DefaultPluginManagerTest.java | 49 ++++++++-- .../scm/plugin/PluginCenterDtoMapperTest.java | 13 ++- .../sonia/scm/plugin/PluginInstallerTest.java | 90 +++++++++++++++++++ 12 files changed, 276 insertions(+), 15 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java diff --git a/scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java b/scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java index b7e7b5e282..1c164a0d81 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java +++ b/scm-core/src/main/java/sonia/scm/plugin/AvailablePluginDescriptor.java @@ -1,5 +1,6 @@ package sonia.scm.plugin; +import java.util.Optional; import java.util.Set; /** @@ -10,11 +11,23 @@ public class AvailablePluginDescriptor implements PluginDescriptor { private final PluginInformation information; private final PluginCondition condition; private final Set dependencies; + private final String url; + private final String checksum; - public AvailablePluginDescriptor(PluginInformation information, PluginCondition condition, Set dependencies) { + public AvailablePluginDescriptor(PluginInformation information, PluginCondition condition, Set dependencies, String url, String checksum) { this.information = information; this.condition = condition; this.dependencies = dependencies; + this.url = url; + this.checksum = checksum; + } + + public String getUrl() { + return url; + } + + public Optional getChecksum() { + return Optional.ofNullable(checksum); } @Override diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index e465c8306e..77a13686c8 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -37,14 +37,20 @@ package sonia.scm.plugin; import com.google.common.collect.ImmutableList; import com.google.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.NotFoundException; //~--- JDK imports ------------------------------------------------------------ import javax.inject.Inject; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import static sonia.scm.ContextEntry.ContextBuilder.entity; + /** * * @author Sebastian Sdorra @@ -52,13 +58,17 @@ import java.util.stream.Collectors; @Singleton public class DefaultPluginManager implements PluginManager { + private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginManager.class); + private final PluginLoader loader; private final PluginCenter center; + private final PluginInstaller installer; @Inject - public DefaultPluginManager(PluginLoader loader, PluginCenter center) { + public DefaultPluginManager(PluginLoader loader, PluginCenter center, PluginInstaller installer) { this.loader = loader; this.center = center; + this.installer = installer; } @Override @@ -98,6 +108,18 @@ public class DefaultPluginManager implements PluginManager { @Override public void install(String name) { + if (getInstalled(name).isPresent()){ + LOG.info("plugin {} is already installed, skipping installation", name); + return; + } + AvailablePlugin plugin = getAvailable(name).orElseThrow(() -> NotFoundException.notFound(entity(AvailablePlugin.class, name))); + Set dependencies = plugin.getDescriptor().getDependencies(); + if (dependencies != null) { + for (String dependency: dependencies){ + install(dependency); + } + } + installer.install(plugin); } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java index afb8e739a0..1a18d696d5 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java @@ -3,6 +3,7 @@ package sonia.scm.plugin; import com.google.common.collect.ImmutableList; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -77,6 +78,8 @@ public final class PluginCenterDto implements Serializable { @XmlAccessorType(XmlAccessType.FIELD) @Getter + @NoArgsConstructor + @AllArgsConstructor static class Link { private String href; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java index 972afa3099..1b84bca147 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java @@ -17,8 +17,9 @@ public abstract class PluginCenterDtoMapper { Set map(PluginCenterDto pluginCenterDto) { Set plugins = new HashSet<>(); for (PluginCenterDto.Plugin plugin : pluginCenterDto.getEmbedded().getPlugins()) { + String url = plugin.getLinks().get("download").getHref(); AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor( - map(plugin), map(plugin.getConditions()), plugin.getDependencies() + map(plugin), map(plugin.getConditions()), plugin.getDependencies(), url, plugin.getSha256() ); plugins.add(new AvailablePlugin(descriptor)); } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java new file mode 100644 index 0000000000..1b04c0adf0 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java @@ -0,0 +1,7 @@ +package sonia.scm.plugin; + +public class PluginChecksumMismatchException extends PluginInstallException { + public PluginChecksumMismatchException(String message) { + super(message); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java new file mode 100644 index 0000000000..cb2a119f62 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java @@ -0,0 +1,7 @@ +package sonia.scm.plugin; + +public class PluginDownloadException extends PluginInstallException { + public PluginDownloadException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java new file mode 100644 index 0000000000..d7a840bdc1 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java @@ -0,0 +1,12 @@ +package sonia.scm.plugin; + +public class PluginInstallException extends RuntimeException { + + public PluginInstallException(String message) { + super(message); + } + + public PluginInstallException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java new file mode 100644 index 0000000000..83650a54a1 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java @@ -0,0 +1,63 @@ +package sonia.scm.plugin; + +import com.google.common.base.Throwables; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; +import com.google.common.io.ByteStreams; +import com.google.common.io.Files; +import sonia.scm.SCMContextProvider; +import sonia.scm.net.ahc.AdvancedHttpClient; +import sonia.scm.util.IOUtil; + +import javax.inject.Inject; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Optional; + +class PluginInstaller { + + private final SCMContextProvider context; + private final AdvancedHttpClient client; + + @Inject + public PluginInstaller(SCMContextProvider context, AdvancedHttpClient client) { + this.context = context; + this.client = client; + } + + public void install(AvailablePlugin plugin) { + File file = createFile(plugin); + try (InputStream input = download(plugin); OutputStream output = new FileOutputStream(file)) { + ByteStreams.copy(input, output); + + verifyChecksum(plugin, file); + } catch (IOException ex) { + throw new PluginDownloadException("failed to install plugin", ex); + } + } + + private void verifyChecksum(AvailablePlugin plugin, File file) throws IOException { + Optional checksum = plugin.getDescriptor().getChecksum(); + if (checksum.isPresent()) { + String calculatedChecksum = Files.hash(file, Hashing.sha256()).toString(); + if (!checksum.get().equalsIgnoreCase(calculatedChecksum)) { + throw new PluginChecksumMismatchException( + String.format("downloaded plugin checksum %s does not match expected %s", calculatedChecksum, checksum.get()) + ); + } + } + } + + private InputStream download(AvailablePlugin plugin) throws IOException { + return client.get(plugin.getDescriptor().getUrl()).request().contentAsStream(); + } + + private File createFile(AvailablePlugin plugin) { + File pluginDirectory = new File(context.getBaseDirectory(), "plugins"); + IOUtil.mkdirs(pluginDirectory); + return new File(pluginDirectory, plugin.getDescriptor().getInformation().getName() + ".smp"); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java index 54954c19d7..87a29a3210 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java @@ -21,7 +21,6 @@ import sonia.scm.plugin.AvailablePluginDescriptor; import sonia.scm.plugin.PluginCondition; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginManager; -import sonia.scm.plugin.PluginState; import sonia.scm.web.VndMediaType; import javax.inject.Provider; @@ -148,7 +147,9 @@ class AvailablePluginResourceTest { } private AvailablePlugin createPlugin(PluginInformation pluginInformation) { - AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor(pluginInformation, new PluginCondition(), Collections.emptySet()); + AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor( + pluginInformation, new PluginCondition(), Collections.emptySet(), "https://download.hitchhiker.com", null + ); return new AvailablePlugin(descriptor); } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java index 22fe370915..c23f9394c3 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -2,7 +2,6 @@ package sonia.scm.plugin; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import org.checkerframework.checker.nullness.Opt; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; @@ -10,16 +9,11 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class DefaultPluginManagerTest { @@ -30,6 +24,9 @@ class DefaultPluginManagerTest { @Mock private PluginCenter center; + @Mock + private PluginInstaller installer; + @InjectMocks private DefaultPluginManager manager; @@ -118,6 +115,44 @@ class DefaultPluginManagerTest { assertThat(available).isEmpty(); } + @Test + void shouldInstallThePlugin() { + AvailablePlugin git = createAvailable("scm-git-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(git)); + + manager.install("scm-git-plugin"); + + verify(installer).install(git); + } + + @Test + void shouldInstallDependingPlugins() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + when(review.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin")); + AvailablePlugin mail = createAvailable("scm-mail-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail)); + + manager.install("scm-review-plugin"); + + verify(installer).install(mail); + verify(installer).install(review); + } + + @Test + void shouldNotInstallAlreadyInstalledDependencies() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + when(review.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin")); + AvailablePlugin mail = createAvailable("scm-mail-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail)); + + InstalledPlugin installedMail = createInstalled("scm-mail-plugin"); + when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedMail)); + + manager.install("scm-review-plugin"); + + verify(installer).install(review); + } + private AvailablePlugin createAvailable(String name) { PluginInformation information = new PluginInformation(); information.setName(name); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java index 0c0b80ccfa..af4aac0e0d 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java @@ -1,5 +1,6 @@ package sonia.scm.plugin; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -42,13 +43,17 @@ class PluginCenterDtoMapperTest { "555000444", new Condition(Collections.singletonList("linux"), "amd64","2.0.0"), ImmutableSet.of("scm-review-plugin"), - new HashMap<>()); + ImmutableMap.of("download", new Link("http://download.hitchhiker.com")) + ); when(dto.getEmbedded().getPlugins()).thenReturn(Collections.singletonList(plugin)); AvailablePluginDescriptor descriptor = mapper.map(dto).iterator().next().getDescriptor(); PluginInformation information = descriptor.getInformation(); PluginCondition condition = descriptor.getCondition(); + assertThat(descriptor.getUrl()).isEqualTo("http://download.hitchhiker.com"); + assertThat(descriptor.getChecksum()).contains("555000444"); + assertThat(information.getAuthor()).isEqualTo(plugin.getAuthor()); assertThat(information.getCategory()).isEqualTo(plugin.getCategory()); assertThat(information.getVersion()).isEqualTo(plugin.getVersion()); @@ -72,7 +77,8 @@ class PluginCenterDtoMapperTest { "12345678aa", new Condition(Collections.singletonList("linux"), "amd64","2.0.0"), ImmutableSet.of("scm-review-plugin"), - new HashMap<>()); + ImmutableMap.of("download", new Link("http://download.hitchhiker.com/review")) + ); Plugin plugin2 = new Plugin( "scm-hitchhiker-plugin", @@ -85,7 +91,8 @@ class PluginCenterDtoMapperTest { "555000444", new Condition(Collections.singletonList("linux"), "amd64","2.0.0"), ImmutableSet.of("scm-review-plugin"), - new HashMap<>()); + ImmutableMap.of("download", new Link("http://download.hitchhiker.com/hitchhiker")) + ); when(dto.getEmbedded().getPlugins()).thenReturn(Arrays.asList(plugin1, plugin2)); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java new file mode 100644 index 0000000000..8801549130 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java @@ -0,0 +1,90 @@ +package sonia.scm.plugin; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.TempDirectory; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.SCMContextProvider; +import sonia.scm.net.ahc.AdvancedHttpClient; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.in; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@ExtendWith({MockitoExtension.class, TempDirectory.class}) +class PluginInstallerTest { + + @Mock + private SCMContextProvider context; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private AdvancedHttpClient client; + + @InjectMocks + private PluginInstaller installer; + + private Path directory; + + @BeforeEach + void setUpContext(@TempDirectory.TempDir Path directory) { + this.directory = directory; + when(context.getBaseDirectory()).thenReturn(directory.toFile()); + } + + @Test + void shouldDownloadPlugin() throws IOException { + mockContent("42"); + + installer.install(createGitPlugin()); + + assertThat(directory.resolve("plugins").resolve("scm-git-plugin.smp")).hasContent("42"); + } + + private void mockContent(String content) throws IOException { + when(client.get("https://download.hitchhiker.com").request().contentAsStream()) + .thenReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); + } + + private AvailablePlugin createGitPlugin() { + return createPlugin( + "scm-git-plugin", + "https://download.hitchhiker.com", + "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049" // 42 + ); + } + + @Test + void shouldThrowPluginDownloadException() throws IOException { + when(client.get("https://download.hitchhiker.com").request()).thenThrow(new IOException("failed to download")); + + assertThrows(PluginDownloadException.class, () -> installer.install(createGitPlugin())); + } + + @Test + void shouldThrowPluginChecksumMismatchException() throws IOException { + mockContent("21"); + + assertThrows(PluginChecksumMismatchException.class, () -> installer.install(createGitPlugin())); + } + + + private AvailablePlugin createPlugin(String name, String url, String checksum) { + PluginInformation information = new PluginInformation(); + information.setName(name); + AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor( + information, null, Collections.emptySet(), url, checksum + ); + return new AvailablePlugin(descriptor); + } +} From 3f183f9f6c2eb8d667717dfe357099e0814bab6e Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Tue, 20 Aug 2019 15:19:02 +0200 Subject: [PATCH 09/27] added key value for child in list --- .../packages/ui-components/src/buttons/ButtonGroup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js b/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js index 2dcb56047c..b73688ebbf 100644 --- a/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js +++ b/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js @@ -14,7 +14,7 @@ class ButtonGroup extends React.Component { const childWrapper = []; React.Children.forEach(children, child => { if (child) { - childWrapper.push(

{child}

); + childWrapper.push(

{child}

); } }); From 02c295207cb5dcb0daf8ba5a82d06f3aea3a2754 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Tue, 20 Aug 2019 15:50:11 +0200 Subject: [PATCH 10/27] added modal with check for plugin installation --- .../packages/ui-types/src/Plugin.js | 2 +- scm-ui/public/locales/de/admin.json | 10 ++- scm-ui/public/locales/en/admin.json | 10 ++- .../admin/plugins/components/PluginEntry.js | 55 +++++++++---- .../admin/plugins/components/PluginModal.js | 78 +++++++++++++++++++ 5 files changed, 137 insertions(+), 18 deletions(-) create mode 100644 scm-ui/src/admin/plugins/components/PluginModal.js diff --git a/scm-ui-components/packages/ui-types/src/Plugin.js b/scm-ui-components/packages/ui-types/src/Plugin.js index 3f4f9858c1..157ef06098 100644 --- a/scm-ui-components/packages/ui-types/src/Plugin.js +++ b/scm-ui-components/packages/ui-types/src/Plugin.js @@ -1,7 +1,6 @@ //@flow import type {Collection, Links} from "./hal"; - export type Plugin = { name: string, version: string, @@ -10,6 +9,7 @@ export type Plugin = { author: string, category: string, avatarUrl: string, + dependencies: string[], _links: Links }; diff --git a/scm-ui/public/locales/de/admin.json b/scm-ui/public/locales/de/admin.json index 54ae1ab91a..3f39de2717 100644 --- a/scm-ui/public/locales/de/admin.json +++ b/scm-ui/public/locales/de/admin.json @@ -29,7 +29,15 @@ "installedNavLink": "Installiert", "availableNavLink": "Verfügbar" }, - "noPlugins": "Keine Plugins gefunden." + "noPlugins": "Keine Plugins gefunden.", + "modal": { + "title": "Plugin installieren", + "dependency": "Abhängigkeit:", + "dependency_plural": "Abhängigkeiten:", + "restart": "Neustarten um Plugin zu aktivieren", + "install": "Installieren", + "abort": "Abbrechen" + } }, "repositoryRole": { "navLink": "Berechtigungsrollen", diff --git a/scm-ui/public/locales/en/admin.json b/scm-ui/public/locales/en/admin.json index 2402f21423..970dc44c7f 100644 --- a/scm-ui/public/locales/en/admin.json +++ b/scm-ui/public/locales/en/admin.json @@ -29,7 +29,15 @@ "installedNavLink": "Installed", "availableNavLink": "Available" }, - "noPlugins": "No plugins found." + "noPlugins": "No plugins found.", + "modal": { + "title": "Install Plugin", + "dependency": "Dependency:", + "dependency_plural": "Dependencies:", + "restart": "Restart to activate", + "install": "Install", + "abort": "Abort" + } }, "repositoryRole": { "navLink": "Permission Roles", diff --git a/scm-ui/src/admin/plugins/components/PluginEntry.js b/scm-ui/src/admin/plugins/components/PluginEntry.js index 7aaeb3f67f..12bc5c35c5 100644 --- a/scm-ui/src/admin/plugins/components/PluginEntry.js +++ b/scm-ui/src/admin/plugins/components/PluginEntry.js @@ -1,9 +1,10 @@ //@flow import React from "react"; import injectSheet from "react-jss"; -import type {Plugin} from "@scm-manager/ui-types"; -import {CardColumn} from "@scm-manager/ui-components"; +import type { Plugin } from "@scm-manager/ui-types"; +import { CardColumn } from "@scm-manager/ui-components"; import PluginAvatar from "./PluginAvatar"; +import PluginModal from "./PluginModal"; type Props = { plugin: Plugin, @@ -12,23 +13,41 @@ type Props = { classes: any }; +type State = { + showModal: boolean +}; + const styles = { link: { - pointerEvents: "cursor" + pointerEvents: "all" } }; -class PluginEntry extends React.Component { +class PluginEntry extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { + showModal: false + }; + } + createAvatar = (plugin: Plugin) => { return ; }; + toggleModal = () => { + this.setState(prevState => ({ + showModal: !prevState.showModal + })); + }; + createContentRight = (plugin: Plugin) => { const { classes } = this.props; if (plugin._links && plugin._links.install && plugin._links.install.href) { return ( -
console.log(plugin._links.install.href) /*TODO trigger plugin installation*/}> - +
+
); } @@ -44,22 +63,28 @@ class PluginEntry extends React.Component { render() { const { plugin } = this.props; + const { showModal } = this.state; const avatar = this.createAvatar(plugin); const contentRight = this.createContentRight(plugin); const footerLeft = this.createFooterLeft(plugin); const footerRight = this.createFooterRight(plugin); + const modal = showModal ? : null; + // TODO: Add link to plugin page below return ( - + <> + + {modal} + ); } } diff --git a/scm-ui/src/admin/plugins/components/PluginModal.js b/scm-ui/src/admin/plugins/components/PluginModal.js new file mode 100644 index 0000000000..aa126f6c58 --- /dev/null +++ b/scm-ui/src/admin/plugins/components/PluginModal.js @@ -0,0 +1,78 @@ +//@flow +import React from "react"; +import { translate } from "react-i18next"; +import type { Plugin } from "@scm-manager/ui-types"; +import { + Button, + ButtonGroup, + Checkbox, + Modal, + SubmitButton +} from "@scm-manager/ui-components"; + +type Props = { + plugin: Plugin, + onSubmit: () => void, + onClose: () => void, + + // context props + t: string => string +}; + +class PluginModal extends React.Component { + renderDependencies() { + const { plugin, t } = this.props; + + let dependencies = null; + if (plugin.dependencies && plugin.dependencies.length > 0) { + dependencies = ( + <> + {t("plugins.modal.dependency", {count: plugin.dependencies.length})} +
    + {plugin.dependencies.map((dependency, index) => { + return
  • {dependency}
  • ; + })} +
+ + ); + } + return dependencies; + } + + render() { + const { onSubmit, onClose, t } = this.props; + + const body = ( + <> + {this.renderDependencies()} + + + ); + + const footer = ( +
+ + +
+ {this.renderError()} ); - const footer = ( -
- - -