diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginFilter.java b/scm-core/src/main/java/sonia/scm/plugin/PluginFilter.java new file mode 100644 index 0000000000..02977b32d5 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginFilter.java @@ -0,0 +1,52 @@ +/** + * 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; + +/** + * + * @author Sebastian Sdorra + */ +public interface PluginFilter +{ + + /** + * Method description + * + * + * @param plugin + * + * @return + */ + public boolean accept(PluginInformation plugin); +} 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 df87714c10..a378851ca8 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -50,6 +50,86 @@ import javax.xml.bind.annotation.XmlRootElement; public class PluginInformation implements Validateable { + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final PluginInformation other = (PluginInformation) obj; + + if ((this.artifactId == null) + ? (other.artifactId != null) + : !this.artifactId.equals(other.artifactId)) + { + return false; + } + + if ((this.author == null) + ? (other.author != null) + : !this.author.equals(other.author)) + { + return false; + } + + if ((this.groupId == null) + ? (other.groupId != null) + : !this.groupId.equals(other.groupId)) + { + return false; + } + + if ((this.version == null) + ? (other.version != null) + : !this.version.equals(other.version)) + { + return false; + } + + return true; + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + int hash = 7; + + hash = 53 * hash + ((this.artifactId != null) + ? this.artifactId.hashCode() + : 0); + hash = 53 * hash + ((this.groupId != null) + ? this.groupId.hashCode() + : 0); + hash = 53 * hash + ((this.version != null) + ? this.version.hashCode() + : 0); + + return hash; + } + + //~--- get methods ---------------------------------------------------------- + /** * Method description * @@ -120,6 +200,17 @@ public class PluginInformation implements Validateable return name; } + /** + * Method description + * + * + * @return + */ + public PluginState getState() + { + return state; + } + /** * Method description * @@ -212,6 +303,17 @@ public class PluginInformation implements Validateable this.name = name; } + /** + * Method description + * + * + * @param state + */ + public void setState(PluginState state) + { + this.state = state; + } + /** * Method description * @@ -251,6 +353,9 @@ public class PluginInformation implements Validateable /** Field description */ private String name; + /** Field description */ + private PluginState state; + /** Field description */ private String url; 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 16c2f81e0f..4b9f8c5973 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java @@ -60,6 +60,14 @@ public interface PluginManager */ public void uninstall(String id); + /** + * Method description + * + * + * @param id + */ + public void update(String id); + //~--- get methods ---------------------------------------------------------- /** @@ -72,6 +80,24 @@ public interface PluginManager */ public PluginInformation get(String id); + /** + * Method description + * + * + * @param filter + * + * @return + */ + public Collection get(PluginFilter filter); + + /** + * Method description + * + * + * @return + */ + public Collection getAll(); + /** * Method description * @@ -80,6 +106,14 @@ public interface PluginManager */ public Collection getAvailable(); + /** + * Method description + * + * + * @return + */ + public Collection getAvailableUpdates(); + /** * Method description * diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginState.java b/scm-core/src/main/java/sonia/scm/plugin/PluginState.java new file mode 100644 index 0000000000..f451c34a75 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginState.java @@ -0,0 +1,43 @@ +/** + * 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; + +/** + * + * @author Sebastian Sdorra + */ +public enum PluginState +{ + CORE, AVAILABLE, INSTALLED, UPDATE_AVAILABLE; +} diff --git a/scm-core/src/main/java/sonia/scm/plugin/StatePluginFilter.java b/scm-core/src/main/java/sonia/scm/plugin/StatePluginFilter.java new file mode 100644 index 0000000000..a92dfe6b14 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/StatePluginFilter.java @@ -0,0 +1,74 @@ +/** + * 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; + +/** + * + * @author Sebastian Sdorra + */ +public class StatePluginFilter implements PluginFilter +{ + + /** + * Constructs ... + * + * + * @param state + */ + public StatePluginFilter(PluginState state) + { + this.state = state; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param plugin + * + * @return + */ + @Override + public boolean accept(PluginInformation plugin) + { + return state == plugin.getState(); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private PluginState state; +} 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 bc64b39e23..dcba7c120a 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java @@ -69,6 +69,10 @@ public class DefaultPluginLoader implements PluginLoader /** Field description */ public static final String PATH_PLUGINCONFIG = "META-INF/scm/plugin.xml"; + /** Field description */ + public static final String REGE_COREPLUGIN = + "^.*(?:/|\\\\)WEB-INF(?:/|\\\\)lib(?:/|\\\\).*\\.jar$"; + /** the logger for DefaultPluginLoader */ private static final Logger logger = LoggerFactory.getLogger(DefaultPluginLoader.class); @@ -196,12 +200,24 @@ public class DefaultPluginLoader implements PluginLoader path = path.substring("jar:file:".length(), path.lastIndexOf("!")); + boolean corePlugin = path.matches(REGE_COREPLUGIN); + if (logger.isInfoEnabled()) { - logger.info("load plugin {}", path); + logger.info("load {}plugin {}", corePlugin + ? "core " + : " ", path); } Plugin plugin = JAXB.unmarshal(url, Plugin.class); + PluginInformation info = plugin.getInformation(); + + if (info != null) + { + info.setState(corePlugin + ? PluginState.CORE + : PluginState.INSTALLED); + } plugin.setPath(path); installedPlugins.add(plugin); 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 78f1aadb03..263ae8b36e 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -48,6 +48,7 @@ import sonia.scm.cache.CacheManager; import sonia.scm.cache.SimpleCache; import sonia.scm.config.ScmConfiguration; import sonia.scm.security.SecurityContext; +import sonia.scm.util.AssertUtil; import sonia.scm.util.SecurityUtil; //~--- JDK imports ------------------------------------------------------------ @@ -79,6 +80,10 @@ public class DefaultPluginManager implements PluginManager private static final Logger logger = LoggerFactory.getLogger(DefaultPluginManager.class); + /** Field description */ + public static final PluginFilter FILTER_UPDATES = + new StatePluginFilter(PluginState.UPDATE_AVAILABLE); + //~--- constructors --------------------------------------------------------- /** @@ -160,6 +165,20 @@ public class DefaultPluginManager implements PluginManager throw new UnsupportedOperationException("Not supported yet."); } + /** + * Method description + * + * + * @param id + */ + @Override + public void update(String id) + { + SecurityUtil.assertIsAdmin(securityContextProvicer); + + throw new UnsupportedOperationException("Not supported yet."); + } + //~--- get methods ---------------------------------------------------------- /** @@ -190,6 +209,52 @@ public class DefaultPluginManager implements PluginManager return result; } + /** + * Method description + * + * + * @param filter + * + * @return + */ + @Override + public Set get(PluginFilter filter) + { + AssertUtil.assertIsNotNull(filter); + SecurityUtil.assertIsAdmin(securityContextProvicer); + + Set infoSet = new HashSet(); + + filter(infoSet, installedPlugins.values(), filter); + filter(infoSet, getPluginCenter().getPlugins(), filter); + + return infoSet; + } + + /** + * Method description + * + * + * @return + */ + @Override + public Collection getAll() + { + SecurityUtil.assertIsAdmin(securityContextProvicer); + + Set infoSet = new HashSet(); + + infoSet.addAll(installedPlugins.values()); + infoSet.addAll(getPluginCenter().getPlugins()); + + for ( PluginInformation pi : infoSet ) + { + System.out.println( pi.getName() + ": " + pi.hashCode() ); + } + + return infoSet; + } + /** * Method description * @@ -215,6 +280,20 @@ public class DefaultPluginManager implements PluginManager return availablePlugins; } + /** + * Method description + * + * + * @return + */ + @Override + public Collection getAvailableUpdates() + { + SecurityUtil.assertIsAdmin(securityContextProvicer); + + return get(FILTER_UPDATES); + } + /** * Method description * @@ -229,6 +308,77 @@ public class DefaultPluginManager implements PluginManager return installedPlugins.values(); } + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param target + * @param source + * @param filter + */ + private void filter(Set target, + Collection source, PluginFilter filter) + { + for (PluginInformation info : source) + { + if (filter.accept(info)) + { + target.add(info); + } + } + } + + /** + * Method description + * + * + * @param available + */ + private void preparePlugin(PluginInformation available) + { + PluginState state = PluginState.AVAILABLE; + + for (PluginInformation installed : installedPlugins.values()) + { + if (isSamePlugin(available, installed)) + { + if (installed.getVersion().equals(available.getVersion())) + { + available.setState(PluginState.INSTALLED); + } + else + { + available.setState(PluginState.UPDATE_AVAILABLE); + } + } + } + + available.setState(state); + } + + /** + * Method description + * + * + * @param pc + */ + private void preparePlugins(PluginCenter pc) + { + Set infoSet = pc.getPlugins(); + + if (infoSet != null) + { + for (PluginInformation available : infoSet) + { + preparePlugin(available); + } + } + } + + //~--- get methods ---------------------------------------------------------- + /** * Method description * @@ -253,6 +403,7 @@ public class DefaultPluginManager implements PluginManager { center = (PluginCenter) unmarshaller.unmarshal( new URL(configuration.getPluginUrl())); + preparePlugins(center); cache.put(PluginCenter.class.getName(), center); if (pluginHandler == null) @@ -272,6 +423,21 @@ public class DefaultPluginManager implements PluginManager return center; } + /** + * Method description + * + * + * @param p1 + * @param p2 + * + * @return + */ + private boolean isSamePlugin(PluginInformation p1, PluginInformation p2) + { + return p1.getGroupId().equals(p2.getGroupId()) + && p1.getArtifactId().equals(p2.getArtifactId()); + } + //~--- fields --------------------------------------------------------------- /** Field description */