diff --git a/scm-core/src/main/java/sonia/scm/plugin/Plugin.java b/scm-core/src/main/java/sonia/scm/plugin/Plugin.java index b8d8a5b623..bf72e5650d 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/Plugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/Plugin.java @@ -35,8 +35,6 @@ package sonia.scm.plugin; //~--- JDK imports ------------------------------------------------------------ -import java.net.URL; - import java.util.Set; import javax.xml.bind.annotation.XmlAccessType; diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 127ce74056..05b5c6c98b 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -89,6 +89,21 @@ public class PluginInformation return groupId; } + /** + * Method description + * + * + * @return + */ + public String getId() + { + StringBuilder id = new StringBuilder(groupId); + + id.append(":").append(artifactId).append(":"); + + return id.append(version).toString(); + } + /** * Method description * diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index ac2530eb07..52b4dd4d77 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -101,6 +101,64 @@ ${ehcache.version} + + + + org.sonatype.aether + aether-api + ${aether.version} + + + + org.sonatype.aether + aether-impl + ${aether.version} + + + + org.apache.maven + maven-aether-provider + ${maven.version} + + + plexus-component-annotations + org.codehaus.plexus + + + + + + org.sonatype.aether + aether-connector-wagon + ${aether.version} + + + + org.apache.maven.wagon + wagon-provider-api + ${wagon.version} + + + + org.apache.maven.wagon + wagon-http-lightweight + ${wagon.version} + + + nekohtml + nekohtml + + + nekohtml + xercesMinimal + + + commons-logging + commons-logging + + + + @@ -116,6 +174,24 @@ + + org.apache.maven.plugins + maven-dependency-plugin + 2.1 + + + compile + + list + + + runtime + ${project.build.directory}/classes/config/dependencies.list + + + + + org.mortbay.jetty maven-jetty-plugin @@ -147,6 +223,12 @@ scm-webapp + + 1.8 + 1.0-beta-7 + 3.0.1 + + diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AvailablePluginResource.java index b3e1e1c887..56259b7fb0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AvailablePluginResource.java @@ -46,9 +46,12 @@ import sonia.scm.plugin.PluginManager; import java.util.Collection; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; /** * @@ -71,6 +74,25 @@ public class AvailablePluginResource this.pluginManager = pluginManager; } + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param id + * + * @return + */ + @POST + @Path("{id}") + public Response install(@PathParam("id") String id) + { + pluginManager.install(id); + + return Response.ok().build(); + } + //~--- get methods ---------------------------------------------------------- /** diff --git a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapListener.java b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapListener.java index a6ece57fe9..90d2f2e0e3 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapListener.java @@ -47,12 +47,19 @@ import java.io.File; import java.io.FilenameFilter; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; + import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; +import javax.xml.bind.JAXB; + /** * * @author Sebastian Sdorra @@ -63,6 +70,9 @@ public class BootstrapListener implements ServletContextListener /** Field description */ public static final String LISTENER = "sonia.scm.ScmContextListener"; + /** Field description */ + public static final String PLUGIN_CLASSPATHFILE = "classpath.xml"; + /** Field description */ public static final String PLUGIN_DIRECTORY = "plugins"; @@ -102,32 +112,23 @@ public class BootstrapListener implements ServletContextListener if (pluginDirectory.exists()) { - try - { - File[] jarFiles = pluginDirectory.listFiles(new JarFilenameFilter()); + File classpathFile = new File(pluginDirectory, PLUGIN_CLASSPATHFILE); - if (Util.isNotEmpty(jarFiles)) + if (classpathFile.exists()) + { + try { - int size = jarFiles.length; - URL[] urls = new URL[size]; + Classpath classpath = JAXB.unmarshal(classpathFile, Classpath.class); - for (int i = 0; i < size; i++) + if (classpath != null) { - urls[i] = jarFiles[i].toURI().toURL(); - - if (logger.isDebugEnabled()) - { - logger.debug("added jar {} to classpath", - urls[i].toExternalForm()); - } + classLoader = createClassLoader(pluginDirectory, classpath); } - - classLoader = new URLClassLoader(urls, getParentClassLoader()); } - } - catch (IOException ex) - { - logger.error(ex.getMessage(), ex); + catch (Exception ex) + { + logger.error(ex.getMessage(), ex); + } } } @@ -150,6 +151,51 @@ public class BootstrapListener implements ServletContextListener scmContextListener.contextInitialized(sce); } + /** + * Method description + * + * + * @param pluginDirectory + * @param classpath + * + * @return + */ + private ClassLoader createClassLoader(File pluginDirectory, + Classpath classpath) + { + List classpathURLs = new LinkedList(); + + for (String path : classpath) + { + if (path.startsWith("/")) + { + path = path.substring(1); + } + + File file = new File(pluginDirectory, path); + + if (file.exists()) + { + try + { + if (logger.isDebugEnabled()) + { + logger.debug("append {} to classpath", file.getPath()); + } + + classpathURLs.add(file.toURI().toURL()); + } + catch (MalformedURLException ex) + { + logger.error(ex.getMessage(), ex); + } + } + } + + return new URLClassLoader(classpathURLs.toArray(new URL[0]), + getParentClassLoader()); + } + //~--- get methods ---------------------------------------------------------- /** @@ -170,35 +216,6 @@ public class BootstrapListener implements ServletContextListener return classLoader; } - //~--- inner classes -------------------------------------------------------- - - /** - * Class description - * - * - * @version Enter version here..., 2010-12-09 - * @author Sebastian Sdorra - */ - private static class JarFilenameFilter implements FilenameFilter - { - - /** - * Method description - * - * - * @param file - * @param name - * - * @return - */ - @Override - public boolean accept(File file, String name) - { - return name.endsWith(".jar"); - } - } - - //~--- fields --------------------------------------------------------------- /** Field description */ diff --git a/scm-webapp/src/main/java/sonia/scm/boot/Classpath.java b/scm-webapp/src/main/java/sonia/scm/boot/Classpath.java new file mode 100644 index 0000000000..ef15c38000 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/boot/Classpath.java @@ -0,0 +1,131 @@ +/** + * 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.boot; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; + +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Sebastian Sdorra + */ +@XmlRootElement(name = "classpath") +@XmlAccessorType(XmlAccessType.FIELD) +public class Classpath implements Iterable +{ + + /** + * Constructs ... + * + */ + public Classpath() {} + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param path + */ + public void add(String path) + { + pathSet.add(path); + } + + /** + * Method description + * + * + * @param file + */ + public void add(File file) + { + pathSet.add(file.getPath()); + } + + /** + * Method description + * + * + * @return + */ + @Override + public Iterator iterator() + { + return pathSet.iterator(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public Set getPathSet() + { + return pathSet; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param pathSet + */ + public void setPathSet(Set pathSet) + { + this.pathSet = pathSet; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + @XmlElement(name = "path") + private Set pathSet = new LinkedHashSet(); +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java new file mode 100644 index 0000000000..be1d4203c8 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java @@ -0,0 +1,155 @@ +/** + * 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.artifact.Artifact; +import org.sonatype.aether.graph.DependencyFilter; +import org.sonatype.aether.graph.DependencyNode; + +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.HashSet; +import java.util.List; +import java.util.Scanner; +import java.util.Set; + +/** + * + * @author Sebastian Sdorra + */ +public class AetherDependencyFilter implements DependencyFilter +{ + + /** Field description */ + public static final String EXCLUDE_LIST = "/config/dependencies.list"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public AetherDependencyFilter() + { + loadExcludes(); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param node + * @param parents + * + * @return + */ + @Override + public boolean accept(DependencyNode node, List parents) + { + Artifact artifact = node.getDependency().getArtifact(); + + return !exludeSet.contains(getId(artifact)); + } + + /** + * Method description + * + */ + private void loadExcludes() + { + Scanner scanner = null; + + try + { + scanner = new Scanner( + AetherDependencyFilter.class.getResourceAsStream(EXCLUDE_LIST)); + + while (scanner.hasNextLine()) + { + parseLine(scanner.nextLine()); + } + } + finally + { + scanner.close(); + } + } + + /** + * Method description + * + * + * @param line + */ + private void parseLine(String line) + { + line = line.trim(); + + if (Util.isNotEmpty(line)) + { + String[] parts = line.split(":"); + + if (parts.length >= 2) + { + exludeSet.add(parts[0].concat(":").concat(parts[1])); + } + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param artifact + * + * @return + */ + private String getId(Artifact artifact) + { + return artifact.getGroupId().concat(":").concat(artifact.getArtifactId()); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private Set exludeSet = new HashSet(); +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java b/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java new file mode 100644 index 0000000000..9db4b38ff5 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java @@ -0,0 +1,282 @@ +/** + * 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.DefaultArtifactDescriptorReader; +import org.apache.maven.repository.internal.DefaultVersionRangeResolver; +import org.apache.maven.repository.internal.DefaultVersionResolver; +import org.apache.maven.repository.internal.MavenRepositorySystemSession; +import org.apache.maven.wagon.Wagon; +import org.apache.maven.wagon.providers.http.LightweightHttpWagon; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.sonatype.aether.RepositorySystem; +import org.sonatype.aether.collection.CollectRequest; +import org.sonatype.aether.connector.wagon.WagonProvider; +import org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory; +import org.sonatype.aether.graph.Dependency; +import org.sonatype.aether.graph.DependencyFilter; +import org.sonatype.aether.impl.ArtifactDescriptorReader; +import org.sonatype.aether.impl.VersionRangeResolver; +import org.sonatype.aether.impl.VersionResolver; +import org.sonatype.aether.impl.internal.DefaultServiceLocator; +import org.sonatype.aether.repository.LocalRepository; +import org.sonatype.aether.repository.RemoteRepository; +import org.sonatype.aether.resolution.ArtifactResult; +import org.sonatype.aether.spi.connector.RepositoryConnectorFactory; +import org.sonatype.aether.util.artifact.DefaultArtifact; + +import sonia.scm.ConfigurationException; +import sonia.scm.SCMContextProvider; +import sonia.scm.boot.BootstrapListener; +import sonia.scm.boot.Classpath; +import sonia.scm.util.IOUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +/** + * + * @author Sebastian Sdorra + */ +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 --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param context + */ + public AetherPluginHandler(SCMContextProvider context) + { + localRepositoryDirectory = new File(context.getBaseDirectory(), + BootstrapListener.PLUGIN_DIRECTORY); + + try + { + jaxbContext = JAXBContext.newInstance(Classpath.class); + } + catch (JAXBException ex) + { + throw new ConfigurationException(ex); + } + + classpathFile = new File(localRepositoryDirectory, + BootstrapListener.PLUGIN_CLASSPATHFILE); + + if (classpathFile.exists()) + { + try + { + classpath = + (Classpath) jaxbContext.createUnmarshaller().unmarshal(classpathFile); + } + catch (JAXBException ex) + { + logger.error(ex.getMessage(), ex); + } + } + + IOUtil.mkdirs(localRepositoryDirectory); + repositorySystem = createRepositorySystem(); + localRepository = new LocalRepository(localRepositoryDirectory); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param gav + */ + public void install(String gav) + { + if (logger.isInfoEnabled()) + { + logger.info("try to install plugin with gav: {}", gav); + } + + Dependency dependency = new Dependency(new DefaultArtifact(gav), + PLUGIN_SCOPE); + CollectRequest request = new CollectRequest(dependency, remoteRepositories); + MavenRepositorySystemSession session = new MavenRepositorySystemSession(); + + session.setLocalRepositoryManager( + repositorySystem.newLocalRepositoryManager(localRepository)); + + try + { + + /* + * DependencyNode node = repositorySystem.collectDependencies(session, + * request).getRoot(); + */ + List artifacts = + repositorySystem.resolveDependencies(session, request, FILTER); + + synchronized (Classpath.class) + { + if (classpath == null) + { + classpath = new Classpath(); + } + + for (ArtifactResult result : artifacts) + { + File file = result.getArtifact().getFile(); + + if (logger.isDebugEnabled()) + { + logger.debug("added {} to classpath", file.getPath()); + } + + classpath.add( + file.getAbsolutePath().substring( + localRepositoryDirectory.getAbsolutePath().length())); + } + + Marshaller marshaller = jaxbContext.createMarshaller(); + + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(classpath, classpathFile); + } + } + catch (Exception ex) + { + throw new PluginLoadException(ex); + } + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param repositories + */ + public void setPluginRepositories(Collection repositories) + { + remoteRepositories = new ArrayList(); + + for (PluginRepository repository : repositories) + { + remoteRepositories.add(new RemoteRepository(repository.getId(), + "default", repository.getUrl())); + } + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private RepositorySystem createRepositorySystem() + { + DefaultServiceLocator locator = new DefaultServiceLocator(); + + locator.addService(VersionResolver.class, DefaultVersionResolver.class); + locator.addService(VersionRangeResolver.class, + DefaultVersionRangeResolver.class); + locator.addService(ArtifactDescriptorReader.class, + DefaultArtifactDescriptorReader.class); + locator.setServices(WagonProvider.class, new WagonProvider() + { + @Override + public Wagon lookup(String roleHint) throws Exception + { + return new LightweightHttpWagon(); + } + @Override + public void release(Wagon wagon) {} + }); + locator.addService(RepositoryConnectorFactory.class, + WagonRepositoryConnectorFactory.class); + + return locator.getService(RepositorySystem.class); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private Classpath classpath; + + /** Field description */ + private File classpathFile; + + /** Field description */ + private JAXBContext jaxbContext; + + /** Field description */ + private LocalRepository localRepository; + + /** Field description */ + private File localRepositoryDirectory; + + /** Field description */ + private List remoteRepositories; + + /** Field description */ + private RepositorySystem repositorySystem; +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 75c4866992..81f948ed72 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -42,6 +42,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.ConfigurationException; +import sonia.scm.SCMContext; import sonia.scm.cache.CacheManager; import sonia.scm.cache.SimpleCache; import sonia.scm.config.ScmConfiguration; @@ -100,9 +101,7 @@ public class DefaultPluginManager implements PluginManager if (info != null) { - String id = getPluginId(info); - - installedPlugins.put(id, plugin.getInformation()); + installedPlugins.put(info.getId(), plugin.getInformation()); } } @@ -128,7 +127,12 @@ public class DefaultPluginManager implements PluginManager @Override public void install(String id) { - throw new UnsupportedOperationException("Not supported yet."); + if (pluginHandler == null) + { + getPluginCenter(); + } + + pluginHandler.install(id); } /** @@ -156,7 +160,19 @@ public class DefaultPluginManager implements PluginManager @Override public PluginInformation get(String id) { - throw new UnsupportedOperationException("Not supported yet."); + PluginInformation result = null; + + for (PluginInformation info : getPluginCenter().getPlugins()) + { + if (id.equals(info.getId())) + { + result = info; + + break; + } + } + + return result; } /** @@ -195,44 +211,37 @@ public class DefaultPluginManager implements PluginManager if (center == null) { - if (logger.isInfoEnabled()) + synchronized (DefaultPluginManager.class) { - logger.info("fetch plugin informations from {}", - configuration.getPluginUrl()); - } + if (logger.isInfoEnabled()) + { + logger.info("fetch plugin informations from {}", + configuration.getPluginUrl()); + } - try - { - center = (PluginCenter) unmarshaller.unmarshal( - new URL(configuration.getPluginUrl())); - cache.put(PluginCenter.class.getName(), center); - } - catch (Exception ex) - { - throw new PluginLoadException(ex); + try + { + center = (PluginCenter) unmarshaller.unmarshal( + new URL(configuration.getPluginUrl())); + cache.put(PluginCenter.class.getName(), center); + + if (pluginHandler == null) + { + pluginHandler = new AetherPluginHandler(SCMContext.getContext()); + } + + pluginHandler.setPluginRepositories(center.getRepositories()); + } + catch (Exception ex) + { + throw new PluginLoadException(ex); + } } } return center; } - /** - * Method description - * - * - * @param info - * - * @return - */ - private String getPluginId(PluginInformation info) - { - StringBuilder id = new StringBuilder(info.getGroupId()); - - id.append(":").append(info.getArtifactId()).append(":"); - - return id.append(info.getVersion()).toString(); - } - //~--- fields --------------------------------------------------------------- /** Field description */ @@ -244,6 +253,9 @@ public class DefaultPluginManager implements PluginManager /** Field description */ private Map installedPlugins; + /** Field description */ + private AetherPluginHandler pluginHandler; + /** Field description */ private Unmarshaller unmarshaller; } diff --git a/scm-webapp/src/main/resources/config/ehcache.xml b/scm-webapp/src/main/resources/config/ehcache.xml index 528780bea1..fa5e5e1456 100644 --- a/scm-webapp/src/main/resources/config/ehcache.xml +++ b/scm-webapp/src/main/resources/config/ehcache.xml @@ -104,19 +104,18 @@ overflowToDisk="true" timeToIdleSeconds="1200" timeToLiveSeconds="2400" - diskPersistent="true" + diskPersistent="false" diskExpiryThreadIntervalSeconds="2400" />