From 3a9c66db25103711f34d371b210695cb80d304d4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 23 May 2013 19:36:21 +0200 Subject: [PATCH] fix classpath generation with manually installed plugins --- .../main/java/sonia/scm/plugin/Aether.java | 214 ++++++++++++++++++ .../scm/plugin/AetherDependencyResolver.java | 210 +++++++++++++++++ .../sonia/scm/plugin/AetherPluginHandler.java | 122 ++-------- .../plugin/StringDependencyGraphDumper.java | 121 ++++++++++ 4 files changed, 570 insertions(+), 97 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/Aether.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/StringDependencyGraphDumper.java diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/Aether.java b/scm-webapp/src/main/java/sonia/scm/plugin/Aether.java new file mode 100644 index 0000000000..d35f6d7d8e --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/Aether.java @@ -0,0 +1,214 @@ +/** + * 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 org.apache.maven.repository.internal.MavenRepositorySystemSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.sonatype.aether.RepositorySystem; +import org.sonatype.aether.RepositorySystemSession; +import org.sonatype.aether.collection.CollectRequest; +import org.sonatype.aether.collection.DependencyCollectionException; +import org.sonatype.aether.graph.Dependency; +import org.sonatype.aether.graph.DependencyFilter; +import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.repository.LocalRepository; +import org.sonatype.aether.repository.LocalRepositoryManager; +import org.sonatype.aether.repository.Proxy; +import org.sonatype.aether.repository.RemoteRepository; +import org.sonatype.aether.repository.RepositoryPolicy; +import org.sonatype.aether.resolution.DependencyRequest; +import org.sonatype.aether.resolution.DependencyResolutionException; +import org.sonatype.aether.util.artifact.DefaultArtifact; +import org.sonatype.aether.util.artifact.JavaScopes; +import org.sonatype.aether.util.filter.DependencyFilterUtils; + +import sonia.scm.config.ScmConfiguration; +import sonia.scm.net.Proxies; + +/** + * + * @author Sebastian Sdorra + */ +public final class Aether +{ + + /** Field description */ + private static final DependencyFilter FILTER = new AetherDependencyFilter(); + + /** + * the logger for Aether + */ + private static final Logger logger = LoggerFactory.getLogger(Aether.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + private Aether() {} + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param gav + * + * @return + */ + public static Dependency createDependency(String gav) + { + return new Dependency(new DefaultArtifact(gav), JavaScopes.RUNTIME); + } + + /** + * Method description + * + * + * @return + */ + public static DependencyFilter createDependencyFilter() + { + return DependencyFilterUtils.andFilter( + DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME), FILTER); + } + + /** + * Method description + * + * + * @param configuration + * @param pluginRepository + * + * @return + */ + public static RemoteRepository createRemoteRepository( + ScmConfiguration configuration, PluginRepository pluginRepository) + { + RemoteRepository remoteRepository = + new RemoteRepository(pluginRepository.getId(), "default", + pluginRepository.getUrl()); + + if (Proxies.isEnabled(configuration, remoteRepository.getHost())) + { + Proxy proxy = DefaultProxySelector.createProxy(configuration); + + if (logger.isDebugEnabled()) + { + logger.debug("enable proxy {} for {}", proxy.getHost(), + pluginRepository.getUrl()); + } + + remoteRepository.setProxy(proxy); + } + + return remoteRepository; + } + + /** + * Method description + * + * + * @return + */ + public static RepositorySystem createRepositorySystem() + { + return new AetherServiceLocator().getService(RepositorySystem.class); + } + + /** + * Method description + * + * + * @param system + * @param repositoryManager + * @param localRepository + * @param configuration + * + * @return + */ + public static RepositorySystemSession createRepositorySystemSession( + RepositorySystem system, LocalRepository localRepository, + ScmConfiguration configuration) + { + MavenRepositorySystemSession session = new MavenRepositorySystemSession(); + + session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN); + + if (configuration.isEnableProxy()) + { + logger.debug("enable proxy selector to collect dependencies"); + session.setProxySelector(new DefaultProxySelector(configuration)); + } + + LocalRepositoryManager localRepositoryManager = + system.newLocalRepositoryManager(localRepository); + + session.setLocalRepositoryManager(localRepositoryManager); + + return session; + } + + /** + * Method description + * + * + * @param system + * @param session + * @param request + * + * + * @return + * @throws DependencyCollectionException + * @throws DependencyResolutionException + */ + public static DependencyNode resolveDependencies(RepositorySystem system, + RepositorySystemSession session, CollectRequest request) + throws DependencyCollectionException, DependencyResolutionException + { + DependencyNode node = system.collectDependencies(session, + request).getRoot(); + DependencyRequest dr = new DependencyRequest(node, + Aether.createDependencyFilter()); + + system.resolveDependencies(session, dr); + + return node; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java new file mode 100644 index 0000000000..4fbafe58e8 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java @@ -0,0 +1,210 @@ +/** + * 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.collect.Lists; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.sonatype.aether.RepositorySystem; +import org.sonatype.aether.RepositorySystemSession; +import org.sonatype.aether.collection.CollectRequest; +import org.sonatype.aether.collection.DependencyCollectionException; +import org.sonatype.aether.graph.Dependency; +import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.repository.LocalRepository; +import org.sonatype.aether.repository.RemoteRepository; +import org.sonatype.aether.resolution.DependencyResolutionException; +import org.sonatype.aether.util.graph.PreorderNodeListGenerator; + +import sonia.scm.config.ScmConfiguration; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * + * @author Sebastian Sdorra + */ +public class AetherDependencyResolver +{ + + /** + * the logger for AetherDependencyResolver + */ + private static final Logger logger = + LoggerFactory.getLogger(AetherDependencyResolver.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param configuration + * @param system + * @param localRepository + * @param remoteRepositories + */ + public AetherDependencyResolver(ScmConfiguration configuration, + RepositorySystem system, LocalRepository localRepository, + List remoteRepositories) + { + this.configuration = configuration; + this.system = system; + this.localRepository = localRepository; + this.remoteRepositories = remoteRepositories; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public String createClassPath() + { + PreorderNodeListGenerator nodeListGenerator = + new PreorderNodeListGenerator(); + + for (DependencyNode node : resolvedNodes) + { + node.accept(nodeListGenerator); + } + + return nodeListGenerator.getClassPath(); + } + + /** + * Method description + * + * + * @param dependency + * + * @throws DependencyCollectionException + * @throws DependencyResolutionException + */ + public void resolveLocalDependency(Dependency dependency) + throws DependencyCollectionException, DependencyResolutionException + { + CollectRequest request = new CollectRequest(); + + request.setRoot(dependency); + resolveDependency(request); + } + + /** + * Method description + * + * + * @param dependency + * + * @throws DependencyCollectionException + * @throws DependencyResolutionException + */ + public void resolveRemoteDependency(Dependency dependency) + throws DependencyCollectionException, DependencyResolutionException + { + resolveDependency(new CollectRequest(dependency, remoteRepositories)); + } + + /** + * Method description + * + * + * @param request + * + * @throws DependencyCollectionException + * @throws DependencyResolutionException + */ + private void resolveDependency(CollectRequest request) + throws DependencyCollectionException, DependencyResolutionException + { + DependencyNode node = Aether.resolveDependencies(system, getSession(), + request); + + if (logger.isTraceEnabled()) + { + StringDependencyGraphDumper dumper = new StringDependencyGraphDumper(); + + node.accept(dumper); + logger.trace(dumper.getGraphAsString()); + } + + resolvedNodes.add(node); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private RepositorySystemSession getSession() + { + if (session == null) + { + session = Aether.createRepositorySystemSession(system, localRepository, + configuration); + } + + return session; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private ScmConfiguration configuration; + + /** Field description */ + private LocalRepository localRepository; + + /** Field description */ + private List remoteRepositories; + + /** Field description */ + private List resolvedNodes = Lists.newArrayList(); + + /** Field description */ + private RepositorySystemSession session; + + /** Field description */ + private RepositorySystem system; +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java b/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java index caa74f6935..b4ac9b8f1c 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java @@ -35,32 +35,21 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- -import org.apache.maven.repository.internal.MavenRepositorySystemSession; +import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonatype.aether.RepositorySystem; -import org.sonatype.aether.collection.CollectRequest; import org.sonatype.aether.graph.Dependency; -import org.sonatype.aether.graph.DependencyFilter; -import org.sonatype.aether.graph.DependencyNode; -import org.sonatype.aether.repository.LocalArtifactRegistration; import org.sonatype.aether.repository.LocalRepository; -import org.sonatype.aether.repository.LocalRepositoryManager; -import org.sonatype.aether.repository.Proxy; import org.sonatype.aether.repository.RemoteRepository; -import org.sonatype.aether.repository.RepositoryPolicy; -import org.sonatype.aether.resolution.DependencyRequest; -import org.sonatype.aether.util.artifact.DefaultArtifact; -import org.sonatype.aether.util.graph.PreorderNodeListGenerator; import sonia.scm.ConfigurationException; import sonia.scm.SCMContextProvider; import sonia.scm.boot.BootstrapListener; import sonia.scm.boot.Classpath; import sonia.scm.config.ScmConfiguration; -import sonia.scm.net.Proxies; import sonia.scm.util.IOUtil; import sonia.scm.util.Util; @@ -68,7 +57,6 @@ import sonia.scm.util.Util; import java.io.File; -import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; @@ -85,16 +73,10 @@ import javax.xml.bind.Marshaller; public class AetherPluginHandler { - /** Field description */ - public static final String PLUGIN_SCOPE = "runtime"; - /** the logger for AetherPluginHandler */ private static final Logger logger = LoggerFactory.getLogger(AetherPluginHandler.class); - /** Field description */ - private static final DependencyFilter FILTER = new AetherDependencyFilter(); - //~--- constructors --------------------------------------------------------- /** @@ -141,7 +123,7 @@ public class AetherPluginHandler } IOUtil.mkdirs(localRepositoryDirectory); - repositorySystem = createRepositorySystem(); + repositorySystem = Aether.createRepositorySystem(); localRepository = new LocalRepository(localRepositoryDirectory); } @@ -160,8 +142,7 @@ public class AetherPluginHandler logger.info("try to install plugin with gav: {}", gav); } - Dependency dependency = new Dependency(new DefaultArtifact(gav), - PLUGIN_SCOPE); + Dependency dependency = Aether.createDependency(gav); List dependencies = getInstalledDependencies(null); collectDependencies(dependency, dependencies); @@ -202,27 +183,12 @@ public class AetherPluginHandler */ public void setPluginRepositories(Collection repositories) { - remoteRepositories = new ArrayList(); + remoteRepositories = Lists.newArrayList(); for (PluginRepository repository : repositories) { - RemoteRepository rr = new RemoteRepository(repository.getId(), "default", - repository.getUrl()); - - if (Proxies.isEnabled(configuration, rr.getHost())) - { - Proxy proxy = DefaultProxySelector.createProxy(configuration); - - if (logger.isDebugEnabled()) - { - logger.debug("enable proxy {} for {}", proxy.getHost(), - repository.getUrl()); - } - - rr.setProxy(proxy); - } - - remoteRepositories.add(rr); + remoteRepositories.add(Aether.createRemoteRepository(configuration, + repository)); } } @@ -234,60 +200,34 @@ public class AetherPluginHandler * * @param dependency * @param dependencies + * @param localDependencies */ private void collectDependencies(Dependency dependency, - List dependencies) + List localDependencies) { - CollectRequest request = new CollectRequest(dependency, dependencies, - remoteRepositories); - MavenRepositorySystemSession session = new MavenRepositorySystemSession(); - - session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN); - - if (configuration.isEnableProxy()) - { - if (logger.isDebugEnabled()) - { - logger.debug("enable proxy selector to collect dependencies"); - } - - session.setProxySelector(new DefaultProxySelector(configuration)); - } - - // register installed to local repository manager - LocalRepositoryManager localRepositoryManager = - repositorySystem.newLocalRepositoryManager(localRepository); - - for (Dependency dep : dependencies) - { - localRepositoryManager.add(session, - new LocalArtifactRegistration(dep.getArtifact())); - } - - session.setLocalRepositoryManager(localRepositoryManager); - try { - DependencyNode node = repositorySystem.collectDependencies(session, - request).getRoot(); - DependencyRequest dr = new DependencyRequest(node, FILTER); + AetherDependencyResolver resolver = + new AetherDependencyResolver(configuration, repositorySystem, + localRepository, remoteRepositories); - repositorySystem.resolveDependencies(session, dr); + resolver.resolveRemoteDependency(dependency); + + for (Dependency localDependency : localDependencies) + { + resolver.resolveLocalDependency(localDependency); + } + + if (classpath == null) + { + classpath = new Classpath(); + } synchronized (Classpath.class) { - if (classpath == null) - { - classpath = new Classpath(); - } - - PreorderNodeListGenerator nodeListGenerator = - new PreorderNodeListGenerator(); - - node.accept(nodeListGenerator); Set classpathSet = - createClasspathSet(nodeListGenerator.getClassPath()); + createClasspathSet(resolver.createClassPath()); classpath.setPathSet(classpathSet); storeClasspath(); @@ -331,17 +271,6 @@ public class AetherPluginHandler return classpathSet; } - /** - * Method description - * - * - * @return - */ - private RepositorySystem createRepositorySystem() - { - return new AetherServiceLocator().getService(RepositorySystem.class); - } - /** * Method description * @@ -368,7 +297,7 @@ public class AetherPluginHandler */ private List getInstalledDependencies(String skipId) { - List dependencies = new ArrayList(); + List dependencies = Lists.newArrayList(); Collection installed = pluginManager.get(new StatePluginFilter(PluginState.INSTALLED)); @@ -380,8 +309,7 @@ public class AetherPluginHandler if (Util.isNotEmpty(id) && ((skipId == null) ||!id.equals(skipId))) { - dependencies.add(new Dependency(new DefaultArtifact(id), - PLUGIN_SCOPE)); + dependencies.add(Aether.createDependency(id)); } } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/StringDependencyGraphDumper.java b/scm-webapp/src/main/java/sonia/scm/plugin/StringDependencyGraphDumper.java new file mode 100644 index 0000000000..faffd5616c --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/StringDependencyGraphDumper.java @@ -0,0 +1,121 @@ +/** + * 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 org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.graph.DependencyVisitor; + +/** + * + * @author Sebastian Sdorra + */ +public class StringDependencyGraphDumper implements DependencyVisitor +{ + + /** Field description */ + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + public void reset() + { + buffer = new StringBuilder(); + } + + /** + * Method description + * + * + * @param node + * + * @return + */ + @Override + public boolean visitEnter(DependencyNode node) + { + buffer.append(currentIndent).append(node).append(LINE_SEPARATOR); + + if (currentIndent.length() <= 0) + { + currentIndent = "+- "; + } + else + { + currentIndent = "| " + currentIndent; + } + + return true; + } + + /** + * Method description + * + * + * @param node + * + * @return + */ + @Override + public boolean visitLeave(DependencyNode node) + { + currentIndent = currentIndent.substring(3, currentIndent.length()); + + return true; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public String getGraphAsString() + { + return buffer.toString(); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private StringBuilder buffer = new StringBuilder(); + + /** Field description */ + private String currentIndent = ""; +}