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; + } + }