From 3f1b6e116091762a0d997cde0a6032835dffdd91 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 1 Dec 2010 14:26:29 +0100 Subject: [PATCH] rebuild plugin system --- .../scm/repository/GitRepositoryHandler.java | 2 + .../java/sonia/scm/web/GitServletModule.java} | 36 +-- .../main/java/sonia/scm/web/GitWebPlugin.java | 94 -------- .../main/resources/META-INF/scm/plugin.xml | 20 +- .../scm/repository/HgRepositoryHandler.java | 2 + .../java/sonia/scm/web/HgServletModule.java | 2 + .../main/resources/META-INF/scm/plugin.xml | 20 +- .../scm/repository/SvnRepositoryHandler.java | 2 + .../java/sonia/scm/web/SvnServletModule.java | 2 + .../main/java/sonia/scm/web/SvnWebPlugin.java | 79 ------ .../main/resources/META-INF/scm/plugin.xml | 18 +- .../services/sonia.scm.web.ScmWebPlugin | 1 - .../scm/plugin/DefaultPluginManager.java | 149 +++++++++--- .../main/java/sonia/scm/plugin/Plugin.java | 94 ++++---- .../sonia/scm/plugin/PluginInformation.java | 176 ++++++++------ .../java/sonia/scm/plugin/PluginManager.java | 33 ++- .../sonia/scm/plugin/PluginResources.java | 40 ++- .../java/sonia/scm/plugin/ext/Extension.java | 34 +-- .../sonia/scm/plugin/ext/ExtensionObject.java | 31 ++- .../scm/plugin/ext/ExtensionProcessor.java | 19 +- .../scm/plugin/ext/ExtensionScanner.java | 66 +++++ .../scm/plugin/ext/JARExtensionScanner.java | 228 ++++++++++++++++++ scm-webapp/pom.xml | 2 +- .../sonia/scm/BindingExtensionProcessor.java | 226 +++++++++++++++++ .../main/java/sonia/scm/ContextListener.java | 116 +-------- .../main/java/sonia/scm/ScmServletModule.java | 124 ++-------- .../rest/resources/RepositoryResource.java | 34 +-- .../scm/plugin/ScriptResourceServlet.java | 86 +++++-- 28 files changed, 1026 insertions(+), 710 deletions(-) rename plugins/{scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java => scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java} (75%) delete mode 100644 plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitWebPlugin.java delete mode 100644 plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnWebPlugin.java delete mode 100644 plugins/scm-svn-plugin/src/main/resources/META-INF/services/sonia.scm.web.ScmWebPlugin rename scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPluginManager.java => scm-core/src/main/java/sonia/scm/plugin/DefaultPluginManager.java (56%) rename scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPlugin.java => scm-core/src/main/java/sonia/scm/plugin/Plugin.java (64%) rename scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPluginContext.java => scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java (60%) rename scm-webapp/src/main/java/sonia/scm/plugin/WebResourceComparator.java => scm-core/src/main/java/sonia/scm/plugin/PluginManager.java (78%) rename scm-web-api/src/main/java/sonia/scm/web/plugin/SecurityConfig.java => scm-core/src/main/java/sonia/scm/plugin/PluginResources.java (70%) rename scm-web-api/src/main/java/sonia/scm/web/plugin/WebResource.java => scm-core/src/main/java/sonia/scm/plugin/ext/Extension.java (83%) rename scm-web-api/src/main/java/sonia/scm/web/plugin/ClasspathWebResource.java => scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionObject.java (79%) rename scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPlugin.java => scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionProcessor.java (86%) create mode 100644 scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionScanner.java create mode 100644 scm-core/src/main/java/sonia/scm/plugin/ext/JARExtensionScanner.java create mode 100644 scm-webapp/src/main/java/sonia/scm/BindingExtensionProcessor.java diff --git a/plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java b/plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java index 3a2d0b623e..275471e385 100644 --- a/plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java +++ b/plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java @@ -40,6 +40,7 @@ import com.google.inject.Singleton; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import sonia.scm.Type; +import sonia.scm.plugin.ext.Extension; //~--- JDK imports ------------------------------------------------------------ @@ -51,6 +52,7 @@ import java.io.IOException; * @author Sebastian Sdorra */ @Singleton +@Extension public class GitRepositoryHandler extends AbstractSimpleRepositoryHandler { diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java b/plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java similarity index 75% rename from plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java rename to plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java index ec988e905a..327d18415b 100644 --- a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgWebPlugin.java +++ b/plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java @@ -29,49 +29,39 @@ * */ + + package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.web.plugin.ClasspathWebResource; -import sonia.scm.web.plugin.ScmWebPlugin; -import sonia.scm.web.plugin.ScmWebPluginContext; +import com.google.inject.servlet.ServletModule; +import sonia.scm.plugin.ext.Extension; + +import sonia.scm.web.filter.BasicAuthenticationFilter; /** * * @author Sebastian Sdorra */ -public class HgWebPlugin implements ScmWebPlugin +@Extension +public class GitServletModule extends ServletModule { /** Field description */ - public static final String SCRIPT = "/sonia/scm/hg.config.js"; + public static final String PATTERN_GIT = "/git/*"; //~--- methods -------------------------------------------------------------- /** * Method description * - * - * @param context */ @Override - public void contextDestroyed(ScmWebPluginContext context) + protected void configureServlets() { - - // do nothing - } - - /** - * Method description - * - * - * @param context - */ - @Override - public void contextInitialized(ScmWebPluginContext context) - { - context.addScriptResource(new ClasspathWebResource(SCRIPT)); - context.addInjectModule(new HgServletModule()); + filter(PATTERN_GIT).through(BasicAuthenticationFilter.class); + filter(PATTERN_GIT).through(GitPermissionFilter.class); + serve(PATTERN_GIT).with(ScmGitServlet.class); } } diff --git a/plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitWebPlugin.java b/plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitWebPlugin.java deleted file mode 100644 index 5f588f4918..0000000000 --- a/plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitWebPlugin.java +++ /dev/null @@ -1,94 +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.web; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.servlet.ServletModule; - -import sonia.scm.web.filter.BasicAuthenticationFilter; -import sonia.scm.web.plugin.ClasspathWebResource; -import sonia.scm.web.plugin.ScmWebPlugin; -import sonia.scm.web.plugin.ScmWebPluginContext; - -/** - * - * @author Sebastian Sdorra - */ -public class GitWebPlugin implements ScmWebPlugin -{ - - /** Field description */ - public static final String PATTERN_GIT = "/git/*"; - - /** Field description */ - public static final String SCRIPT = "/sonia/scm/git.config.js"; - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param context - */ - @Override - public void contextDestroyed(ScmWebPluginContext context) - { - - // do nothing - } - - /** - * Method description - * - * - * @param context - */ - @Override - public void contextInitialized(ScmWebPluginContext context) - { - context.addScriptResource(new ClasspathWebResource(SCRIPT)); - context.addInjectModule(new ServletModule() - { - @Override - protected void configureServlets() - { - filter(PATTERN_GIT).through(BasicAuthenticationFilter.class); - filter(PATTERN_GIT).through(GitPermissionFilter.class); - serve(PATTERN_GIT).with(ScmGitServlet.class); - } - }); - } -} diff --git a/plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml b/plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml index 8a726e5431..df1e450d34 100644 --- a/plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -40,12 +40,18 @@ Purpose of the document follows. --> - + - sonia.scm.web.GitWebPlugin + + ${project.name} + ${project.description} + Sebastian Sdorra + ${project.version} + http://bitbucket.org/sdorra/scm-manager + - - sonia.scm.repository.GitRepositoryHandler - - - + + + + + diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index 5a2ac3541e..aeed42614d 100644 --- a/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -42,6 +42,7 @@ import sonia.scm.io.ExtendedCommand; import sonia.scm.io.INIConfiguration; import sonia.scm.io.INIConfigurationWriter; import sonia.scm.io.INISection; +import sonia.scm.plugin.ext.Extension; //~--- JDK imports ------------------------------------------------------------ @@ -53,6 +54,7 @@ import java.io.IOException; * @author Sebastian Sdorra */ @Singleton +@Extension public class HgRepositoryHandler extends AbstractSimpleRepositoryHandler { diff --git a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java index 2371d8639f..7013f8a8db 100644 --- a/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java +++ b/plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java @@ -36,6 +36,7 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.servlet.ServletModule; +import sonia.scm.plugin.ext.Extension; import sonia.scm.web.filter.BasicAuthenticationFilter; @@ -43,6 +44,7 @@ import sonia.scm.web.filter.BasicAuthenticationFilter; * * @author Sebastian Sdorra */ +@Extension public class HgServletModule extends ServletModule { diff --git a/plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml b/plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml index 8d36910a9f..934dea3d5d 100644 --- a/plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -40,12 +40,18 @@ Purpose of the document follows. --> - + - sonia.scm.web.HgWebPlugin + + scm-hg-plugin + Mercurial Plugin + Sebastian Sdorra + 1.0-M3-SNAPSHOT + http://bitbucket.org/sdorra/scm-manager + - - sonia.scm.repository.HgRepositoryHandler - - - + + + + + diff --git a/plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java b/plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java index 5e609d977e..a113c7003a 100644 --- a/plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java +++ b/plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java @@ -41,6 +41,7 @@ import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; import sonia.scm.Type; +import sonia.scm.plugin.ext.Extension; //~--- JDK imports ------------------------------------------------------------ @@ -52,6 +53,7 @@ import java.io.IOException; * @author Sebastian Sdorra */ @Singleton +@Extension public class SvnRepositoryHandler extends AbstractSimpleRepositoryHandler { diff --git a/plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java b/plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java index 1ec471d3d9..c0a30ff6c3 100644 --- a/plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java +++ b/plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java @@ -43,11 +43,13 @@ import sonia.scm.web.filter.BasicAuthenticationFilter; import java.util.HashMap; import java.util.Map; +import sonia.scm.plugin.ext.Extension; /** * * @author Sebastian Sdorra */ +@Extension public class SvnServletModule extends ServletModule { diff --git a/plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnWebPlugin.java b/plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnWebPlugin.java deleted file mode 100644 index d4b49cbc98..0000000000 --- a/plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnWebPlugin.java +++ /dev/null @@ -1,79 +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.web; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.web.plugin.ClasspathWebResource; -import sonia.scm.web.plugin.ScmWebPlugin; -import sonia.scm.web.plugin.ScmWebPluginContext; - -/** - * - * @author Sebastian Sdorra - */ -public class SvnWebPlugin implements ScmWebPlugin -{ - - /** Field description */ - public static final String SCRIPT = "/sonia/scm/svn.config.js"; - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param context - */ - @Override - public void contextDestroyed(ScmWebPluginContext context) - { - - // do nothing - } - - /** - * Method description - * - * - * @param context - */ - @Override - public void contextInitialized(ScmWebPluginContext context) - { - context.addScriptResource(new ClasspathWebResource(SCRIPT)); - context.addInjectModule(new SvnServletModule()); - } -} diff --git a/plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml b/plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml index 0d982f2881..282efba831 100644 --- a/plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -40,12 +40,18 @@ Purpose of the document follows. --> - + - sonia.scm.web.SvnWebPlugin + + ${project.name} + ${project.description} + Sebastian Sdorra + ${project.version} + http://bitbucket.org/sdorra/scm-manager + - - sonia.scm.repository.SvnRepositoryHandler - + + + - + \ No newline at end of file diff --git a/plugins/scm-svn-plugin/src/main/resources/META-INF/services/sonia.scm.web.ScmWebPlugin b/plugins/scm-svn-plugin/src/main/resources/META-INF/services/sonia.scm.web.ScmWebPlugin deleted file mode 100644 index db9924452d..0000000000 --- a/plugins/scm-svn-plugin/src/main/resources/META-INF/services/sonia.scm.web.ScmWebPlugin +++ /dev/null @@ -1 +0,0 @@ -sonia.scm.web.SvnWebPlugin \ No newline at end of file diff --git a/scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPluginManager.java b/scm-core/src/main/java/sonia/scm/plugin/DefaultPluginManager.java similarity index 56% rename from scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPluginManager.java rename to scm-core/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 0393af83b2..1fe0eb0b1d 100644 --- a/scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPluginManager.java +++ b/scm-core/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -29,21 +29,31 @@ * */ -package sonia.scm.web.plugin; + + +package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.plugin.ext.ExtensionObject; +import sonia.scm.plugin.ext.ExtensionProcessor; +import sonia.scm.plugin.ext.JARExtensionScanner; +import sonia.scm.util.IOUtil; + //~--- JDK imports ------------------------------------------------------------ +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URL; +import java.util.Collection; import java.util.Enumeration; -import java.util.LinkedHashSet; +import java.util.HashSet; import java.util.Set; import javax.xml.bind.JAXB; @@ -52,15 +62,38 @@ import javax.xml.bind.JAXB; * * @author Sebastian Sdorra */ -public class SCMPluginManager +public class DefaultPluginManager implements PluginManager { + /** Field description */ + public static final String DEFAULT_PACKAGE = "sonia.scm"; + /** Field description */ public static final String PATH_PLUGINCONFIG = "META-INF/scm/plugin.xml"; - /** Field description */ + /** the logger for DefaultPluginManager */ private static final Logger logger = - LoggerFactory.getLogger(SCMPluginManager.class); + LoggerFactory.getLogger(DefaultPluginManager.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public DefaultPluginManager() + { + ClassLoader classLoader = getClassLoader(); + + try + { + load(classLoader); + } + catch (IOException ex) + { + throw new RuntimeException(ex); + } + } //~--- methods -------------------------------------------------------------- @@ -68,20 +101,64 @@ public class SCMPluginManager * Method description * * - * @throws IOException + * @param processor */ - public void load() throws IOException + @Override + public void processExtensions(ExtensionProcessor processor) { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Set extensions = new HashSet(); + ClassLoader classLoader = getClassLoader(); + JARExtensionScanner scanner = new JARExtensionScanner(); - if (classLoader == null) + for (Plugin plugin : plugins) { - classLoader = SCMPluginManager.class.getClassLoader(); + InputStream input = null; + + try + { + Set packageSet = plugin.getPackageSet(); + + if (packageSet == null) + { + packageSet = new HashSet(); + } + + packageSet.add(DEFAULT_PACKAGE); + input = new FileInputStream(plugin.getPath()); + scanner.processExtensions(classLoader, extensions, input, packageSet); + } + catch (IOException ex) + { + logger.error(ex.getMessage(), ex); + } + finally + { + IOUtil.close(input); + } } - load(classLoader); + for (ExtensionObject exo : extensions) + { + processor.processExtension(exo.getExtension(), exo.getExtensionClass()); + } } + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public Collection getPlugins() + { + return plugins; + } + + //~--- methods -------------------------------------------------------------- + /** * Method description * @@ -90,7 +167,7 @@ public class SCMPluginManager * * @throws IOException */ - public void load(ClassLoader classLoader) throws IOException + private void load(ClassLoader classLoader) throws IOException { Enumeration urlEnum = classLoader.getResources(PATH_PLUGINCONFIG); @@ -105,21 +182,6 @@ public class SCMPluginManager } } - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public Set getPlugins() - { - return plugins; - } - - //~--- methods -------------------------------------------------------------- - /** * Method description * @@ -130,13 +192,20 @@ public class SCMPluginManager { try { - SCMPlugin plugin = JAXB.unmarshal(url, SCMPlugin.class); + + // jar:file:/some/path/file.jar!/META-INF/scm/plugin.xml + String path = url.toExternalForm(); + + path = path.substring("jar:file:".length(), path.lastIndexOf("!")); if (logger.isInfoEnabled()) { - logger.info("load plugin {}", url.toExternalForm()); + logger.info("load plugin {}", path); } + Plugin plugin = JAXB.unmarshal(url, Plugin.class); + + plugin.setPath(path); plugins.add(plugin); } catch (Exception ex) @@ -145,8 +214,28 @@ public class SCMPluginManager } } + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private ClassLoader getClassLoader() + { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if (classLoader == null) + { + classLoader = DefaultPluginManager.class.getClassLoader(); + } + + return classLoader; + } + //~--- fields --------------------------------------------------------------- /** Field description */ - private Set plugins = new LinkedHashSet(); + private Set plugins = new HashSet(); } diff --git a/scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPlugin.java b/scm-core/src/main/java/sonia/scm/plugin/Plugin.java similarity index 64% rename from scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPlugin.java rename to scm-core/src/main/java/sonia/scm/plugin/Plugin.java index 44a27f8b04..b8d8a5b623 100644 --- a/scm-web-api/src/main/java/sonia/scm/web/plugin/SCMPlugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/Plugin.java @@ -31,15 +31,12 @@ -package sonia.scm.web.plugin; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.repository.RepositoryHandler; +package sonia.scm.plugin; //~--- JDK imports ------------------------------------------------------------ -import java.util.HashSet; +import java.net.URL; + import java.util.Set; import javax.xml.bind.annotation.XmlAccessType; @@ -47,36 +44,26 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; /** * * @author Sebastian Sdorra */ -@XmlRootElement(name = "plugin-config") +@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) -public class SCMPlugin +public class Plugin { - /** - * Constructs ... - * - */ - public SCMPlugin() - { - repositoryHandlers = new HashSet>(); - } - - //~--- get methods ---------------------------------------------------------- - /** * Method description * * * @return */ - public Set> getRepositoryHandlers() + public PluginInformation getInformation() { - return repositoryHandlers; + return information; } /** @@ -85,9 +72,9 @@ public class SCMPlugin * * @return */ - public SecurityConfig getSecurityConfig() + public Set getPackageSet() { - return securityConfig; + return packageSet; } /** @@ -96,9 +83,20 @@ public class SCMPlugin * * @return */ - public Class getWebPlugin() + public String getPath() { - return webPlugin; + return path; + } + + /** + * Method description + * + * + * @return + */ + public PluginResources getResources() + { + return resources; } //~--- set methods ---------------------------------------------------------- @@ -107,48 +105,60 @@ public class SCMPlugin * Method description * * - * @param repositoryHandlers + * @param information */ - public void setRepositoryHandlers( - Set> repositoryHandlers) + public void setInformation(PluginInformation information) { - this.repositoryHandlers = repositoryHandlers; + this.information = information; } /** * Method description * * - * @param securityConfig + * @param packageSet */ - public void setSecurityConfig(SecurityConfig securityConfig) + public void setPackageSet(Set packageSet) { - this.securityConfig = securityConfig; + this.packageSet = packageSet; } /** * Method description * * - * @param webPlugin + * @param path */ - public void setWebPlugin(Class webPlugin) + public void setPath(String path) { - this.webPlugin = webPlugin; + this.path = path; + } + + /** + * Method description + * + * + * @param resources + */ + public void setResources(PluginResources resources) + { + this.resources = resources; } //~--- fields --------------------------------------------------------------- /** Field description */ - @XmlElementWrapper(name = "repository-handlers") - @XmlElement(name = "repository-handler") - private Set> repositoryHandlers; + private PluginInformation information; /** Field description */ - @XmlElement(name = "security") - private SecurityConfig securityConfig; + @XmlElement(name = "package") + @XmlElementWrapper(name = "packages") + private Set packageSet; /** Field description */ - @XmlElement(name = "web-plugin") - private Class webPlugin; + @XmlTransient + private String path; + + /** Field description */ + private PluginResources resources; } diff --git a/scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPluginContext.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java similarity index 60% rename from scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPluginContext.java rename to scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 7914825f03..c62ad1d3e3 100644 --- a/scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPluginContext.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -29,95 +29,26 @@ * */ -package sonia.scm.web.plugin; -//~--- non-JDK imports -------------------------------------------------------- -import com.google.inject.Module; - -//~--- JDK imports ------------------------------------------------------------ - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import javax.servlet.ServletContext; +package sonia.scm.plugin; /** * * @author Sebastian Sdorra */ -public class ScmWebPluginContext +public class PluginInformation { - /** - * Constructs ... - * - * - * @param servletContext - */ - public ScmWebPluginContext(ServletContext servletContext) - { - this.servletContext = servletContext; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param module - */ - public void addInjectModule(Module module) - { - injectModules.add(module); - } - - /** - * Method description - * - * - * @param resource - */ - public void addScriptResource(WebResource resource) - { - scriptResources.add(resource); - } - - /** - * Method description - * - * - * @param module - */ - public void removeInjectModule(Module module) - { - injectModules.remove(module); - } - - /** - * Method description - * - * - * @param resource - */ - public void removeScriptResource(WebResource resource) - { - scriptResources.remove(resource); - } - - //~--- get methods ---------------------------------------------------------- - /** * Method description * * * @return */ - public Collection getInjectModules() + public String getAuthor() { - return injectModules; + return author; } /** @@ -126,9 +57,9 @@ public class ScmWebPluginContext * * @return */ - public Collection getScriptResources() + public String getDescription() { - return scriptResources; + return description; } /** @@ -137,19 +68,104 @@ public class ScmWebPluginContext * * @return */ - public ServletContext getServletContext() + public String getName() { - return servletContext; + return name; + } + + /** + * Method description + * + * + * @return + */ + public String getUrl() + { + return url; + } + + /** + * Method description + * + * + * @return + */ + public String getVersion() + { + return version; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param author + */ + public void setAuthor(String author) + { + this.author = author; + } + + /** + * Method description + * + * + * @param description + */ + public void setDescription(String description) + { + this.description = description; + } + + /** + * Method description + * + * + * @param name + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Method description + * + * + * @param url + */ + public void setUrl(String url) + { + this.url = url; + } + + /** + * Method description + * + * + * @param version + */ + public void setVersion(String version) + { + this.version = version; } //~--- fields --------------------------------------------------------------- /** Field description */ - private Set scriptResources = new HashSet(); + private String author; /** Field description */ - private Set injectModules = new HashSet(); + private String description; /** Field description */ - private ServletContext servletContext; + private String name; + + /** Field description */ + private String url; + + /** Field description */ + private String version; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/WebResourceComparator.java b/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java similarity index 78% rename from scm-webapp/src/main/java/sonia/scm/plugin/WebResourceComparator.java rename to scm-core/src/main/java/sonia/scm/plugin/PluginManager.java index 24c4f11b3c..614d6681d2 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/WebResourceComparator.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginManager.java @@ -35,39 +35,36 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.web.plugin.WebResource; +import com.google.inject.Binder; //~--- JDK imports ------------------------------------------------------------ -import java.io.Serializable; - -import java.util.Comparator; +import java.util.Collection; +import sonia.scm.plugin.ext.ExtensionProcessor; /** * * @author Sebastian Sdorra */ -public class WebResourceComparator - implements Comparator, Serializable +public interface PluginManager { - /** Field description */ - private static final long serialVersionUID = -5916499507958881372L; - - //~--- methods -------------------------------------------------------------- - /** * Method description * * - * @param resource - * @param resource1 + * @param binder + */ + public void processExtensions(ExtensionProcessor processor); + + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * * * @return */ - @Override - public int compare(WebResource resource, WebResource resource1) - { - return resource.getId().compareTo(resource1.getId()); - } + public Collection getPlugins(); } diff --git a/scm-web-api/src/main/java/sonia/scm/web/plugin/SecurityConfig.java b/scm-core/src/main/java/sonia/scm/plugin/PluginResources.java similarity index 70% rename from scm-web-api/src/main/java/sonia/scm/web/plugin/SecurityConfig.java rename to scm-core/src/main/java/sonia/scm/plugin/PluginResources.java index fd30f9fd52..1ede0da4b3 100644 --- a/scm-web-api/src/main/java/sonia/scm/web/plugin/SecurityConfig.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginResources.java @@ -29,15 +29,14 @@ * */ -package sonia.scm.web.plugin; -//~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.security.EncryptionHandler; -import sonia.scm.web.security.Authenticator; +package sonia.scm.plugin; //~--- JDK imports ------------------------------------------------------------ +import java.util.Set; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; @@ -46,8 +45,8 @@ import javax.xml.bind.annotation.XmlElement; * * @author Sebastian Sdorra */ -@XmlAccessorType(XmlAccessType.FIELD) -public class SecurityConfig +@XmlAccessorType(XmlAccessType.NONE) +public class PluginResources { /** @@ -56,9 +55,9 @@ public class SecurityConfig * * @return */ - public Class getAuthenticator() + public Set getScriptResources() { - return authenticator; + return scriptResources; } /** @@ -67,9 +66,9 @@ public class SecurityConfig * * @return */ - public Class getEncryptionHandler() + public Set getStylesheetResources() { - return encryptionHandler; + return stylesheetResources; } //~--- set methods ---------------------------------------------------------- @@ -78,32 +77,31 @@ public class SecurityConfig * Method description * * - * @param authenticator + * @param scriptResources */ - public void setAuthenticator(Class authenticator) + public void setScriptResources(Set scriptResources) { - this.authenticator = authenticator; + this.scriptResources = scriptResources; } /** * Method description * * - * @param encryptionHandler + * @param stylesheetResources */ - public void setEncryptionHandler( - Class encryptionHandler) + public void setStylesheetResources(Set stylesheetResources) { - this.encryptionHandler = encryptionHandler; + this.stylesheetResources = stylesheetResources; } //~--- fields --------------------------------------------------------------- /** Field description */ - @XmlElement(name = "authenticator") - private Class authenticator; + @XmlElement(name = "script") + private Set scriptResources; /** Field description */ - @XmlElement(name = "encryption-handler") - private Class encryptionHandler; + @XmlElement(name = "stylesheet") + private Set stylesheetResources; } diff --git a/scm-web-api/src/main/java/sonia/scm/web/plugin/WebResource.java b/scm-core/src/main/java/sonia/scm/plugin/ext/Extension.java similarity index 83% rename from scm-web-api/src/main/java/sonia/scm/web/plugin/WebResource.java rename to scm-core/src/main/java/sonia/scm/plugin/ext/Extension.java index a0e4f501cf..437088cb1f 100644 --- a/scm-web-api/src/main/java/sonia/scm/web/plugin/WebResource.java +++ b/scm-core/src/main/java/sonia/scm/plugin/ext/Extension.java @@ -31,35 +31,21 @@ -package sonia.scm.web.plugin; +package sonia.scm.plugin.ext; //~--- JDK imports ------------------------------------------------------------ -import java.io.IOException; -import java.io.InputStream; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * * @author Sebastian Sdorra */ -public interface WebResource -{ - - /** - * Method description - * - * - * @return - * - * @throws IOException - */ - public InputStream getContent() throws IOException; - - /** - * Method description - * - * - * @return - */ - public String getId(); -} +@Documented +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface Extension {} diff --git a/scm-web-api/src/main/java/sonia/scm/web/plugin/ClasspathWebResource.java b/scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionObject.java similarity index 79% rename from scm-web-api/src/main/java/sonia/scm/web/plugin/ClasspathWebResource.java rename to scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionObject.java index 5bfcc840cb..6493cac33e 100644 --- a/scm-web-api/src/main/java/sonia/scm/web/plugin/ClasspathWebResource.java +++ b/scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionObject.java @@ -31,28 +31,26 @@ -package sonia.scm.web.plugin; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.InputStream; +package sonia.scm.plugin.ext; /** * * @author Sebastian Sdorra */ -public class ClasspathWebResource implements WebResource +public class ExtensionObject { /** * Constructs ... * * - * @param contentPath + * @param extension + * @param extensionClass */ - public ClasspathWebResource(String contentPath) + public ExtensionObject(Extension extension, Class extensionClass) { - this.contentPath = contentPath; + this.extension = extension; + this.extensionClass = extensionClass; } //~--- get methods ---------------------------------------------------------- @@ -63,10 +61,9 @@ public class ClasspathWebResource implements WebResource * * @return */ - @Override - public InputStream getContent() + public Extension getExtension() { - return ClasspathWebResource.class.getResourceAsStream(contentPath); + return extension; } /** @@ -75,14 +72,16 @@ public class ClasspathWebResource implements WebResource * * @return */ - @Override - public String getId() + public Class getExtensionClass() { - return contentPath; + return extensionClass; } //~--- fields --------------------------------------------------------------- /** Field description */ - private String contentPath; + private Extension extension; + + /** Field description */ + private Class extensionClass; } diff --git a/scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPlugin.java b/scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionProcessor.java similarity index 86% rename from scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPlugin.java rename to scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionProcessor.java index 689e376bb1..c52e9002ba 100644 --- a/scm-web-api/src/main/java/sonia/scm/web/plugin/ScmWebPlugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionProcessor.java @@ -29,28 +29,23 @@ * */ -package sonia.scm.web.plugin; + + +package sonia.scm.plugin.ext; /** * * @author Sebastian Sdorra */ -public interface ScmWebPlugin +public interface ExtensionProcessor { /** * Method description * * - * @param context + * @param extension + * @param extensionClass */ - public void contextDestroyed(ScmWebPluginContext context); - - /** - * Method description - * - * - * @param context - */ - public void contextInitialized(ScmWebPluginContext context); + public void processExtension(Extension extension, Class extensionClass); } diff --git a/scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionScanner.java b/scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionScanner.java new file mode 100644 index 0000000000..de99db626f --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/ext/ExtensionScanner.java @@ -0,0 +1,66 @@ +/** + * 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.ext; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.InputStream; + +import java.util.Collection; + +/** + * + * @author Sebastian Sdorra + */ +public interface ExtensionScanner +{ + + /** + * Method description + * + * + * + * @param classLoader + * @param extensionObjects + * @param input + * @param packagess + * + * @throws IOException + */ + public void processExtensions(ClassLoader classLoader, + Collection extensionObjects, + InputStream input, Collection packagess) + throws IOException; +} diff --git a/scm-core/src/main/java/sonia/scm/plugin/ext/JARExtensionScanner.java b/scm-core/src/main/java/sonia/scm/plugin/ext/JARExtensionScanner.java new file mode 100644 index 0000000000..55b60b69d2 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/ext/JARExtensionScanner.java @@ -0,0 +1,228 @@ +/** + * 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.ext; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.util.IOUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.InputStream; + +import java.util.Collection; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + * + * @author Sebastian Sdorra + */ +public class JARExtensionScanner implements ExtensionScanner +{ + + /** the logger for JARExtensionScanner */ + private static final Logger logger = + LoggerFactory.getLogger(JARExtensionScanner.class); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * + * @param classLoader + * @param extensionObjects + * @param inputStream + * @param packages + * + * @throws IOException + */ + @Override + public void processExtensions(ClassLoader classLoader, + Collection extensionObjects, + InputStream inputStream, + Collection packages) + throws IOException + { + JarInputStream input = null; + + try + { + input = new JarInputStream(inputStream); + + JarEntry entry = input.getNextJarEntry(); + + while (entry != null) + { + if (!entry.isDirectory()) + { + processEntry(classLoader, extensionObjects, packages, entry); + } + + entry = input.getNextJarEntry(); + } + } + finally + { + IOUtil.close(input); + } + } + + /** + * Method description + * + * + * @param classLoader + * @param name + * + * @return + */ + private Class createClass(ClassLoader classLoader, String name) + { + Class clazz = null; + + try + { + clazz = classLoader.loadClass(name); + } + catch (Exception ex) + { + logger.error(ex.getMessage(), ex); + } + + return clazz; + } + + /** + * Method description + * + * + * + * @param classLoader + * @param extensionObjects + * @param packages + * @param entry + */ + private void processEntry(ClassLoader classLoader, + Collection extensionObjects, + Collection packages, JarEntry entry) + { + String name = entry.getName(); + + if (name.endsWith(".class")) + { + name = getClassName(name); + + if (isManagedClass(packages, name)) + { + Class managedClass = createClass(classLoader, name); + + if (managedClass != null) + { + processManagedClass(extensionObjects, managedClass); + } + } + } + } + + /** + * Method description + * + * + * @param extensionObjects + * @param managedClass + */ + private void processManagedClass( + Collection extensionObjects, Class managedClass) + { + Extension extension = managedClass.getAnnotation(Extension.class); + + if (extension != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("found extension class {}", managedClass.getName()); + } + + extensionObjects.add(new ExtensionObject(extension, managedClass)); + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param name + * + * @return + */ + private String getClassName(String name) + { + return name.replaceAll("/", ".").substring(0, name.length() - 6); + } + + /** + * Method description + * + * + * @param packages + * @param name + * + * @return + */ + private boolean isManagedClass(Collection packages, String name) + { + boolean result = false; + + for (String pkg : packages) + { + if (name.startsWith(pkg)) + { + result = true; + + break; + } + } + + return result; + } +} diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 98653cb5f2..cb0ab4af6e 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -42,7 +42,7 @@ scm-hg-plugin 1.0-M3-SNAPSHOT - + sonia.scm.plugins scm-svn-plugin diff --git a/scm-webapp/src/main/java/sonia/scm/BindingExtensionProcessor.java b/scm-webapp/src/main/java/sonia/scm/BindingExtensionProcessor.java new file mode 100644 index 0000000000..8046bb72a8 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/BindingExtensionProcessor.java @@ -0,0 +1,226 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.multibindings.Multibinder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.plugin.ext.Extension; +import sonia.scm.plugin.ext.ExtensionProcessor; +import sonia.scm.repository.RepositoryHandler; +import sonia.scm.security.EncryptionHandler; +import sonia.scm.web.security.Authenticator; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author Sebastian Sdorra + */ +public class BindingExtensionProcessor implements ExtensionProcessor +{ + + /** the logger for BindingExtensionProcessor */ + private static final Logger logger = + LoggerFactory.getLogger(BindingExtensionProcessor.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + */ + public BindingExtensionProcessor() + { + this.moduleSet = new HashSet(); + this.extensions = new HashSet>(); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param binder + */ + @SuppressWarnings("unchecked") + public void bindExtensions(Binder binder) + { + Multibinder repositoryHandlers = + Multibinder.newSetBinder(binder, RepositoryHandler.class); + + for (Class extensionClass : extensions) + { + try + { + if (RepositoryHandler.class.isAssignableFrom(extensionClass)) + { + if (logger.isInfoEnabled()) + { + logger.info("bind RepositoryHandler {}", extensionClass.getName()); + } + + binder.bind(extensionClass); + + repositoryHandlers.addBinding().to(extensionClass); + + } + else if (EncryptionHandler.class.isAssignableFrom(extensionClass)) + { + bind(binder, EncryptionHandler.class, extensionClass); + } + else if (Authenticator.class.isAssignableFrom(extensionClass)) + { + bind(binder, Authenticator.class, extensionClass); + } + else + { + if (logger.isInfoEnabled()) + { + logger.info("bind {}", extensionClass.getName()); + } + + binder.bind(extensionClass); + } + } + catch (Exception ex) + { + logger.error(ex.getMessage(), ex); + } + } + } + + /** + * Method description + * + * + * @param extension + * @param extensionClass + */ + @Override + public void processExtension(Extension extension, Class extensionClass) + { + if (Module.class.isAssignableFrom(extensionClass)) + { + if (logger.isInfoEnabled()) + { + logger.info("add GuiceModule {}", extensionClass.getName()); + } + + addModuleClass(extensionClass); + } + else + { + extensions.add(extensionClass); + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public Set getModuleSet() + { + return moduleSet; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param extensionClass + */ + private void addModuleClass(Class extensionClass) + { + try + { + Module module = extensionClass.newInstance(); + + moduleSet.add(module); + } + catch (Exception ex) + { + logger.error(ex.getMessage(), ex); + } + } + + /** + * Method description + * + * + * + * @param binder + * @param type + * @param bindingType + * @param + * + * @return + */ + private void bind(Binder binder, Class type, + Class bindingType) + { + if (logger.isDebugEnabled()) + { + logger.debug("bind Authenticator {}", type.getName(), + bindingType.getName()); + } + + binder.bind(type).to(bindingType); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private Set> extensions; + + /** Field description */ + private Set moduleSet; +} diff --git a/scm-webapp/src/main/java/sonia/scm/ContextListener.java b/scm-webapp/src/main/java/sonia/scm/ContextListener.java index e00ea55ae5..dc6e95b7f9 100644 --- a/scm-webapp/src/main/java/sonia/scm/ContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ContextListener.java @@ -40,29 +40,16 @@ import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.servlet.GuiceServletContextListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import sonia.scm.plugin.DefaultPluginManager; +import sonia.scm.plugin.PluginManager; import sonia.scm.repository.RepositoryManager; import sonia.scm.user.UserManager; -import sonia.scm.util.Util; -import sonia.scm.web.plugin.SCMPlugin; -import sonia.scm.web.plugin.SCMPluginManager; -import sonia.scm.web.plugin.ScmWebPlugin; -import sonia.scm.web.plugin.ScmWebPluginContext; import sonia.scm.web.security.Authenticator; //~--- JDK imports ------------------------------------------------------------ -import java.io.IOException; - import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; - -import javax.servlet.ServletContextEvent; /** * @@ -71,77 +58,6 @@ import javax.servlet.ServletContextEvent; public class ContextListener extends GuiceServletContextListener { - /** the logger for ContextListener */ - private static final Logger logger = - LoggerFactory.getLogger(ContextListener.class); - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param servletContextEvent - */ - @Override - public void contextDestroyed(ServletContextEvent servletContextEvent) - { - for (ScmWebPlugin plugin : webPluginSet) - { - plugin.contextDestroyed(webPluginContext); - } - - super.contextDestroyed(servletContextEvent); - } - - /** - * Method description - * - * - * @param servletContextEvent - */ - @Override - public void contextInitialized(ServletContextEvent servletContextEvent) - { - pluginManager = new SCMPluginManager(); - - try - { - pluginManager.load(); - webPluginContext = - new ScmWebPluginContext(servletContextEvent.getServletContext()); - - for (SCMPlugin plugin : pluginManager.getPlugins()) - { - try - { - webPluginSet.add(plugin.getWebPlugin().newInstance()); - } - catch (InstantiationException ex) - { - logger.error(ex.getMessage(), ex); - } - catch (IllegalAccessException ex) - { - logger.error(ex.getMessage(), ex); - } - } - - for (ScmWebPlugin plugin : webPluginSet) - { - plugin.contextInitialized(webPluginContext); - } - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - - super.contextInitialized(servletContextEvent); - } - - //~--- get methods ---------------------------------------------------------- - /** * Method description * @@ -151,18 +67,19 @@ public class ContextListener extends GuiceServletContextListener @Override protected Injector getInjector() { - List modules = new ArrayList(); + PluginManager manager = new DefaultPluginManager(); + BindingExtensionProcessor bindExtProcessor = + new BindingExtensionProcessor(); - modules.add(new ScmServletModule(pluginManager, webPluginContext)); + manager.processExtensions(bindExtProcessor); - Collection pluginModules = webPluginContext.getInjectModules(); + ScmServletModule main = new ScmServletModule(manager, bindExtProcessor); + List moduleList = + new ArrayList(bindExtProcessor.getModuleSet()); - if (Util.isNotEmpty(pluginModules)) - { - modules.addAll(pluginModules); - } + moduleList.add(0, main); - Injector injector = Guice.createInjector(modules); + Injector injector = Guice.createInjector(moduleList); // init RepositoryManager injector.getInstance(RepositoryManager.class).init(SCMContext.getContext()); @@ -175,15 +92,4 @@ public class ContextListener extends GuiceServletContextListener return injector; } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private SCMPluginManager pluginManager; - - /** Field description */ - private ScmWebPluginContext webPluginContext; - - /** Field description */ - private Set webPluginSet = new LinkedHashSet(); } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 7ad5041fbc..a66b0a84e1 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -35,7 +35,6 @@ package sonia.scm; //~--- non-JDK imports -------------------------------------------------------- -import com.google.inject.multibindings.Multibinder; import com.google.inject.servlet.ServletModule; import org.slf4j.Logger; @@ -46,20 +45,16 @@ import sonia.scm.cache.CacheManager; import sonia.scm.cache.EhCacheManager; import sonia.scm.config.ScmConfiguration; import sonia.scm.filter.SecurityFilter; +import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.ScriptResourceServlet; -import sonia.scm.repository.RepositoryHandler; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.xml.XmlRepositoryManager; import sonia.scm.security.EncryptionHandler; import sonia.scm.security.MessageDigestEncryptionHandler; +import sonia.scm.security.SecurityContext; import sonia.scm.user.UserManager; import sonia.scm.user.xml.XmlUserManager; import sonia.scm.util.DebugServlet; -import sonia.scm.util.Util; -import sonia.scm.web.plugin.SCMPlugin; -import sonia.scm.web.plugin.SCMPluginManager; -import sonia.scm.web.plugin.ScmWebPluginContext; -import sonia.scm.web.plugin.SecurityConfig; import sonia.scm.web.security.Authenticator; import sonia.scm.web.security.BasicSecurityContext; import sonia.scm.web.security.WebSecurityContext; @@ -74,14 +69,10 @@ import com.sun.jersey.spi.container.servlet.ServletContainer; import java.io.File; -import java.util.Collection; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.Map; -import java.util.Set; import javax.xml.bind.JAXB; -import sonia.scm.security.SecurityContext; /** * @@ -131,15 +122,14 @@ public class ScmServletModule extends ServletModule * Constructs ... * * - * - * @param pluginManager - * @param webPluginContext + * @param manager + * @param bindExtProcessor */ - ScmServletModule(SCMPluginManager pluginManager, - ScmWebPluginContext webPluginContext) + ScmServletModule(PluginManager manager, + BindingExtensionProcessor bindExtProcessor) { - this.pluginManager = pluginManager; - this.webPluginContext = webPluginContext; + this.pluginManager = manager; + this.bindExtProcessor = bindExtProcessor; } //~--- methods -------------------------------------------------------------- @@ -158,19 +148,18 @@ public class ScmServletModule extends ServletModule ScmConfiguration config = getScmConfiguration(context); bind(ScmConfiguration.class).toInstance(config); - - // bind(EncryptionHandler.class).to(MessageDigestEncryptionHandler.class); - // bind(Authenticator.class).to(XmlAuthenticator.class); + bind(PluginManager.class).toInstance(pluginManager); + bind(EncryptionHandler.class).to(MessageDigestEncryptionHandler.class); + bind(Authenticator.class).to(XmlAuthenticator.class); bind(SecurityContext.class).to(BasicSecurityContext.class); bind(WebSecurityContext.class).to(BasicSecurityContext.class); - loadPlugins(pluginManager); + bindExtProcessor.bindExtensions(binder()); bind(CacheManager.class).to(EhCacheManager.class); // bind(RepositoryManager.class).annotatedWith(Undecorated.class).to( // BasicRepositoryManager.class); bind(RepositoryManager.class).to(XmlRepositoryManager.class); bind(UserManager.class).to(XmlUserManager.class); - bind(ScmWebPluginContext.class).toInstance(webPluginContext); // filter(PATTERN_RESTAPI).through(LoggingFilter.class); @@ -206,91 +195,6 @@ public class ScmServletModule extends ServletModule serve(PATTERN_RESTAPI).with(GuiceContainer.class, params); } - /** - * Method description - * - * - * @param repositoryHandlerBinder - * @param handlerSet - */ - private void bindRepositoryHandlers( - Multibinder repositoryHandlerBinder, - Set> handlerSet) - { - for (Class handlerClass : handlerSet) - { - if (logger.isInfoEnabled()) - { - logger.info("load RepositoryHandler {}", handlerClass.getName()); - } - - bind(handlerClass); - repositoryHandlerBinder.addBinding().to(handlerClass); - } - } - - /** - * Method description - * - * - * @param pluginManager - */ - private void loadPlugins(SCMPluginManager pluginManager) - { - Set pluginSet = pluginManager.getPlugins(); - - if (Util.isNotEmpty(pluginSet)) - { - - // repository handlers - Multibinder repositoryHandlerBinder = - Multibinder.newSetBinder(binder(), RepositoryHandler.class); - Set> repositoryHandlerSet = - new LinkedHashSet>(); - - // security stuff - Class encryptionHandler = - MessageDigestEncryptionHandler.class; - Class authenticator = XmlAuthenticator.class; - - for (SCMPlugin plugin : pluginSet) - { - Collection> pluginRepositoryHandlers = - plugin.getRepositoryHandlers(); - - if (Util.isNotEmpty(pluginRepositoryHandlers)) - { - repositoryHandlerSet.addAll(pluginRepositoryHandlers); - } - - SecurityConfig securityConfig = plugin.getSecurityConfig(); - - if (securityConfig != null) - { - Class enc = - securityConfig.getEncryptionHandler(); - - if (enc != null) - { - encryptionHandler = enc; - } - - Class auth = - securityConfig.getAuthenticator(); - - if (auth != null) - { - authenticator = auth; - } - } - } - - bind(EncryptionHandler.class).to(encryptionHandler); - bind(Authenticator.class).to(authenticator); - bindRepositoryHandlers(repositoryHandlerBinder, repositoryHandlerSet); - } - } - //~--- get methods ---------------------------------------------------------- /** @@ -329,8 +233,8 @@ public class ScmServletModule extends ServletModule //~--- fields --------------------------------------------------------------- /** Field description */ - private SCMPluginManager pluginManager; + private BindingExtensionProcessor bindExtProcessor; /** Field description */ - private ScmWebPluginContext webPluginContext; + private PluginManager pluginManager; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java index 7a99c904b2..c55e11f5cb 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java @@ -213,25 +213,27 @@ public class RepositoryResource extends AbstractResource */ private void appendUrl(Repository repository) { - StringBuilder url = new StringBuilder(request.getScheme()); - - url.append("://").append(configuration.getServername()); - url.append(":").append(request.getLocalPort()); - - String ctxPath = request.getContextPath(); - - if (ctxPath.endsWith("/")) - { - ctxPath = ctxPath.substring(0, ctxPath.length() - 1); - } - - url.append(ctxPath); - RepositoryHandler handler = repositoryManager.getHandler(repository.getType()); - url.append(handler.createResourcePath(repository)); - repository.setUrl(url.toString()); + if (handler != null) + { + StringBuilder url = new StringBuilder(request.getScheme()); + + url.append("://").append(configuration.getServername()); + url.append(":").append(request.getLocalPort()); + + String ctxPath = request.getContextPath(); + + if (ctxPath.endsWith("/")) + { + ctxPath = ctxPath.substring(0, ctxPath.length() - 1); + } + + url.append(ctxPath); + url.append(handler.createResourcePath(repository)); + repository.setUrl(url.toString()); + } } //~--- fields --------------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/ScriptResourceServlet.java b/scm-webapp/src/main/java/sonia/scm/plugin/ScriptResourceServlet.java index bbd8c7b54f..837d5e24f5 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/ScriptResourceServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/ScriptResourceServlet.java @@ -40,8 +40,6 @@ import com.google.inject.Singleton; import sonia.scm.util.IOUtil; import sonia.scm.util.Util; -import sonia.scm.web.plugin.ScmWebPluginContext; -import sonia.scm.web.plugin.WebResource; //~--- JDK imports ------------------------------------------------------------ @@ -49,10 +47,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.Set; +import java.util.TreeSet; import javax.servlet.ServletException; @@ -70,6 +67,20 @@ public class ScriptResourceServlet extends AbstractResourceServlet /** Field description */ private static final long serialVersionUID = -5769146163848821050L; + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param manager + */ + @Inject + public ScriptResourceServlet(PluginManager manager) + { + this.manager = manager; + } + //~--- methods -------------------------------------------------------------- /** @@ -89,19 +100,13 @@ public class ScriptResourceServlet extends AbstractResourceServlet "function sayPluginHello(){ alert('Plugin Hello !'); }".concat( System.getProperty("line.separator")).getBytes()); - Collection scriptResources = - webPluginContext.getScriptResources(); + Collection scriptResources = getScriptResources(); if (Util.isNotEmpty(scriptResources)) { - List resourceList = - new ArrayList(scriptResources); - - Collections.sort(resourceList, new WebResourceComparator()); - - for (WebResource scriptResource : resourceList) + for (String resource : scriptResources) { - appendResource(stream, scriptResource); + appendResource(stream, resource); } } } @@ -132,10 +137,10 @@ public class ScriptResourceServlet extends AbstractResourceServlet * @throws IOException * @throws ServletException */ - private void appendResource(OutputStream stream, WebResource script) + private void appendResource(OutputStream stream, String script) throws ServletException, IOException { - InputStream input = script.getContent(); + InputStream input = ScriptResourceServlet.class.getResourceAsStream(script); if (input != null) { @@ -150,9 +155,54 @@ public class ScriptResourceServlet extends AbstractResourceServlet } } + /** + * Method description + * + * + * @param resources + * @param plugin + */ + private void processPlugin(Set resources, Plugin plugin) + { + PluginResources pluginResources = plugin.getResources(); + + if (pluginResources != null) + { + Set scriptResources = pluginResources.getScriptResources(); + + if (scriptResources != null) + { + resources.addAll(scriptResources); + } + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private Collection getScriptResources() + { + Set resources = new TreeSet(); + Collection plugins = manager.getPlugins(); + + if (plugins != null) + { + for (Plugin plugin : plugins) + { + processPlugin(resources, plugin); + } + } + + return resources; + } + //~--- fields --------------------------------------------------------------- /** Field description */ - @Inject - private ScmWebPluginContext webPluginContext; + private PluginManager manager; }