diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java index be1d4203c8..b2e8d94f1c 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java @@ -83,9 +83,19 @@ public class AetherDependencyFilter implements DependencyFilter @Override public boolean accept(DependencyNode node, List parents) { - Artifact artifact = node.getDependency().getArtifact(); + boolean result = true; - return !exludeSet.contains(getId(artifact)); + if ((node != null) && (node.getDependency() != null)) + { + Artifact artifact = node.getDependency().getArtifact(); + + if (artifact != null) + { + result = !exludeSet.contains(getId(artifact)); + } + } + + return result; } /** 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 d5fd5a6e49..fb54acdc3b 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java @@ -51,21 +51,23 @@ 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.graph.DependencyNode; 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 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.util.IOUtil; +import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ @@ -73,8 +75,9 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -103,10 +106,14 @@ public class AetherPluginHandler * Constructs ... * * + * + * @param pluginManager * @param context */ - public AetherPluginHandler(SCMContextProvider context) + public AetherPluginHandler(PluginManager pluginManager, + SCMContextProvider context) { + this.pluginManager = pluginManager; localRepositoryDirectory = new File(context.getBaseDirectory(), BootstrapListener.PLUGIN_DIRECTORY); @@ -157,96 +164,34 @@ public class AetherPluginHandler Dependency dependency = new Dependency(new DefaultArtifact(gav), PLUGIN_SCOPE); - CollectRequest request = new CollectRequest(dependency, remoteRepositories); - MavenRepositorySystemSession session = new MavenRepositorySystemSession(); + List dependencies = getInstalledDependencies(null); - session.setLocalRepositoryManager( - repositorySystem.newLocalRepositoryManager(localRepository)); + collectDependencies(dependency, dependencies); - 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())); - } - - storeClasspath(); - } - } - catch (Exception ex) - { - throw new PluginException(ex); - } + } /** * TODO: remove dependencies and remove files * * - * @param pluginPath + * + * @param id */ - public void uninstall(String pluginPath) + public void uninstall(String id) { if (logger.isInfoEnabled()) { - logger.info("try to uninstall plugin: {}", pluginPath); + logger.info("try to uninstall plugin: {}", id); } if (classpath != null) { synchronized (Classpath.class) { - Iterator iterator = classpath.iterator(); + List dependencies = getInstalledDependencies(id); - while (iterator.hasNext()) - { - String path = iterator.next(); - - if (pluginPath.contains(path)) - { - if (logger.isInfoEnabled()) - { - logger.info("remove {} from classpath", path); - } - - iterator.remove(); - - break; - } - } - - try - { - storeClasspath(); - } - catch (JAXBException ex) - { - throw new PluginException(ex); - } + collectDependencies(null, dependencies); } } } @@ -272,6 +217,86 @@ public class AetherPluginHandler //~--- methods -------------------------------------------------------------- + /** + * Method description + * + * + * @param dependency + * @param dependencies + */ + private void collectDependencies(Dependency dependency, + List dependencies) + { + CollectRequest request = new CollectRequest(dependency, dependencies, + remoteRepositories); + MavenRepositorySystemSession session = new MavenRepositorySystemSession(); + + session.setLocalRepositoryManager( + repositorySystem.newLocalRepositoryManager(localRepository)); + + try + { + DependencyNode node = repositorySystem.collectDependencies(session, + request).getRoot(); + + repositorySystem.resolveDependencies(session, node, FILTER); + + synchronized (Classpath.class) + { + if (classpath == null) + { + classpath = new Classpath(); + } + + PreorderNodeListGenerator nodeListGenerator = + new PreorderNodeListGenerator(); + + node.accept(nodeListGenerator); + + Set classpathSet = + createClasspathSet(nodeListGenerator.getClassPath()); + + classpath.setPathSet(classpathSet); + storeClasspath(); + } + } + catch (Exception ex) + { + throw new PluginException(ex); + } + } + + /** + * Method description + * + * + * @param classpathString + * + * @return + */ + private Set createClasspathSet(String classpathString) + { + if (logger.isDebugEnabled()) + { + logger.debug("set new plugin classpath: {}", classpathString); + } + + Set classpathSet = new LinkedHashSet(); + + if (Util.isNotEmpty(classpathString)) + { + String[] classpathParts = classpathString.split(File.pathSeparator); + int prefixLength = localRepositoryDirectory.getAbsolutePath().length(); + + for (String classpathPart : classpathParts) + { + classpathSet.add(classpathPart.substring(prefixLength)); + } + } + + return classpathSet; + } + /** * Method description * @@ -317,6 +342,42 @@ public class AetherPluginHandler marshaller.marshal(classpath, classpathFile); } + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * + * @param skipId + * @return + */ + private List getInstalledDependencies(String skipId) + { + List dependencies = new ArrayList(); + Collection installed = + pluginManager.get(new StatePluginFilter(PluginState.INSTALLED)); + + if (installed != null) + { + for (PluginInformation plugin : installed) + { + String id = plugin.getId(); + + if ((skipId == null) ||!id.equals(skipId)) + { + if (Util.isNotEmpty(id)) + { + dependencies.add(new Dependency(new DefaultArtifact(id), + PLUGIN_SCOPE)); + } + } + } + } + + return dependencies; + } + //~--- fields --------------------------------------------------------------- /** Field description */ @@ -334,6 +395,9 @@ public class AetherPluginHandler /** Field description */ private File localRepositoryDirectory; + /** Field description */ + private PluginManager pluginManager; + /** Field description */ private List remoteRepositories; 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 44756baec4..9d23e2fdb3 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -50,6 +50,7 @@ import sonia.scm.config.ScmConfiguration; import sonia.scm.security.SecurityContext; import sonia.scm.util.AssertUtil; import sonia.scm.util.SecurityUtil; +import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ @@ -154,12 +155,25 @@ public class DefaultPluginManager implements PluginManager { SecurityUtil.assertIsAdmin(securityContextProvicer); - if (pluginHandler == null) - { - getPluginCenter(); - } + PluginCenter center = getPluginCenter(); pluginHandler.install(id); + + for (PluginInformation plugin : center.getPlugins()) + { + String pluginId = plugin.getId(); + + if (Util.isNotEmpty(pluginId) && pluginId.equals(id)) + { + plugin.setState(PluginState.INSTALLED); + + // ugly workaround + Plugin newPlugin = new Plugin(); + + newPlugin.setInformation(plugin); + installedPlugins.put(id, newPlugin); + } + } } /** @@ -182,10 +196,12 @@ public class DefaultPluginManager implements PluginManager if (pluginHandler == null) { - pluginHandler = new AetherPluginHandler(SCMContext.getContext()); + getPluginCenter(); } - pluginHandler.uninstall(plugin.getPath()); + pluginHandler.uninstall(id); + installedPlugins.remove(id); + preparePlugins(getPluginCenter()); } /** @@ -465,7 +481,8 @@ public class DefaultPluginManager implements PluginManager if (pluginHandler == null) { - pluginHandler = new AetherPluginHandler(SCMContext.getContext()); + pluginHandler = new AetherPluginHandler(this, + SCMContext.getContext()); } pluginHandler.setPluginRepositories(center.getRepositories()); diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.plugin.js b/scm-webapp/src/main/webapp/resources/js/sonia.plugin.js index 84990fa916..65c614925d 100644 --- a/scm-webapp/src/main/webapp/resources/js/sonia.plugin.js +++ b/scm-webapp/src/main/webapp/resources/js/sonia.plugin.js @@ -46,6 +46,8 @@ Sonia.plugin.Store = Ext.extend(Sonia.rest.JsonStore, { }); +Sonia.plugin.StoreInstance = null; + Sonia.plugin.GetPluginId = function(data){ return data.groupId + ':' + data.artifactId + ':' + data.version; } @@ -84,6 +86,7 @@ Sonia.plugin.installPlugin = function(pluginId){ loadingBox.hide(); Ext.MessageBox.alert('Plugin successfully installed', 'Restart the applicationserver to activate the plugin.'); + Sonia.plugin.StoreInstance.reload(); }, failure: function(){ if ( debug ){ @@ -126,6 +129,7 @@ Sonia.plugin.uninstallPlugin = function(pluginId){ loadingBox.hide(); Ext.MessageBox.alert('Plugin successfully uninstalled', 'Restart the applicationserver to activate the changes.'); + Sonia.plugin.StoreInstance.reload(); }, failure: function(){ if ( debug ){ @@ -168,6 +172,7 @@ Sonia.plugin.updatePlugin = function(pluginId){ loadingBox.hide(); Ext.MessageBox.alert('Plugin successfully updated', 'Restart the applicationserver to activate the changes.'); + Sonia.plugin.StoreInstance.reload(); }, failure: function(){ if ( debug ){ @@ -192,10 +197,11 @@ Sonia.plugin.Grid = Ext.extend(Sonia.rest.Grid, { initComponent: function(){ - var pluginStore = new Sonia.plugin.Store({ + Sonia.plugin.StoreInstance = new Sonia.plugin.Store({ url: restUrl + 'plugins.json' }); + var pluginColModel = new Ext.grid.ColumnModel({ defaults: { sortable: true, @@ -215,7 +221,7 @@ Sonia.plugin.Grid = Ext.extend(Sonia.rest.Grid, { var config = { autoExpandColumn: 'description', - store: pluginStore, + store: Sonia.plugin.StoreInstance, colModel: pluginColModel, emptyText: 'No plugins avaiable' };