diff --git a/changelog.html b/changelog.html
index cc13ba27b727147a211e0814d10ac3eaebc35049..9546fd5c4f00ac1b8a65f30c285d1d19e24e9b4c 100644
--- a/changelog.html
+++ b/changelog.html
@@ -61,6 +61,9 @@ Upcoming changes
isPartial() check for matrix builds now only reference active configurations.
(issue 10197)
+
+ Maven jobs building plugins were no longer identified as upstream snapshot dependencies.
+ (issue 10530)
diff --git a/core/src/main/java/hudson/model/DependencyGraph.java b/core/src/main/java/hudson/model/DependencyGraph.java
index bb4f9e7e82b6d70a2894a6a90022aede1d90c882..c24ba0f96d0fcd87864f54316e830b70cbc0174c 100644
--- a/core/src/main/java/hudson/model/DependencyGraph.java
+++ b/core/src/main/java/hudson/model/DependencyGraph.java
@@ -66,7 +66,7 @@ import java.util.Stack;
* @see Jenkins#getDependencyGraph()
* @author Kohsuke Kawaguchi
*/
-public final class DependencyGraph implements Comparator {
+public class DependencyGraph implements Comparator {
private Map> forward = new HashMap>();
private Map> backward = new HashMap>();
@@ -79,6 +79,9 @@ public final class DependencyGraph implements Comparator {
* Builds the dependency graph.
*/
public DependencyGraph() {
+ }
+
+ public void build() {
// Set full privileges while computing to avoid missing any projects the current user cannot see.
// Use setContext (NOT getContext().setAuthentication()) so we don't affect concurrent threads for same HttpSession.
SecurityContext saveCtx = SecurityContextHolder.getContext();
@@ -87,7 +90,7 @@ public final class DependencyGraph implements Comparator {
NotSerilizableSecurityContext system = new NotSerilizableSecurityContext();
system.setAuthentication(ACL.SYSTEM);
SecurityContextHolder.setContext(system);
- for( AbstractProject p : Jenkins.getInstance().getAllItems(AbstractProject.class) )
+ for( AbstractProject p : getAllProjects() )
p.buildDependencyGraph(this);
forward = finalize(forward);
@@ -99,6 +102,10 @@ public final class DependencyGraph implements Comparator {
SecurityContextHolder.setContext(saveCtx);
}
}
+
+ Collection getAllProjects() {
+ return Jenkins.getInstance().getAllItems(AbstractProject.class);
+ }
/**
* Special constructor for creating an empty graph
diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java
index 9c02535a65d5e0ced1de820c7eb4e6e61ee7dcf9..1bec37d82ef06e7d3d38288fa38d38eaa28aa200 100644
--- a/core/src/main/java/jenkins/model/Jenkins.java
+++ b/core/src/main/java/jenkins/model/Jenkins.java
@@ -3297,7 +3297,11 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup4.8.2
test
+
+ org.mockito
+ mockito-core
+ 1.8.5
+ test
+
+
+ org.powermock
+ powermock-module-junit4
+ 1.4.9
+ test
+
+
+ org.powermock
+ powermock-api-mockito
+ 1.4.9
+ test
+
diff --git a/maven-plugin/src/main/java/hudson/maven/MavenModule.java b/maven-plugin/src/main/java/hudson/maven/MavenModule.java
index c8731ddfc8f3bc1239fc6cf51bf824646635100f..0005b457e24b87792e738f02bd7621f17b68a2ac 100644
--- a/maven-plugin/src/main/java/hudson/maven/MavenModule.java
+++ b/maven-plugin/src/main/java/hudson/maven/MavenModule.java
@@ -24,42 +24,55 @@
package hudson.maven;
import hudson.CopyOnWrite;
-import hudson.Util;
import hudson.Functions;
+import hudson.Util;
import hudson.maven.reporters.MavenMailer;
-import hudson.model.*;
+import hudson.model.AbstractProject;
+import hudson.model.Action;
+import hudson.model.DependencyGraph;
+import hudson.model.Descriptor;
import hudson.model.Descriptor.FormException;
-import jenkins.model.Jenkins;
+import hudson.model.Item;
+import hudson.model.ItemGroup;
+import hudson.model.JDK;
+import hudson.model.Job;
+import hudson.model.Label;
+import hudson.model.Node;
+import hudson.model.Resource;
+import hudson.model.Saveable;
import hudson.tasks.LogRotator;
-import hudson.tasks.Publisher;
import hudson.tasks.Maven.MavenInstallation;
+import hudson.tasks.Publisher;
import hudson.util.AlternativeUiTextProvider;
import hudson.util.DescribableList;
-import org.apache.maven.project.MavenProject;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.StaplerResponse;
-import org.kohsuke.stapler.export.Exported;
-
-import com.google.common.util.concurrent.MoreExecutors;
-import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.servlet.ServletException;
+
+import jenkins.model.Jenkins;
+
+import org.apache.maven.project.MavenProject;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.kohsuke.stapler.export.Exported;
+
/**
* {@link Job} that builds projects based on Maven2.
*
* @author Kohsuke Kawaguchi
*/
-public final class MavenModule extends AbstractMavenProject implements Saveable {
+public class MavenModule extends AbstractMavenProject implements Saveable {
private DescribableList> reporters =
new DescribableList>(this);
@@ -79,6 +92,13 @@ public final class MavenModule extends AbstractMavenProject modules = new HashMap();
- for (MavenModule m : Jenkins.getInstance().getAllItems(MavenModule.class)) {
+ for (MavenModule m : getAllMavenModules()) {
if(!m.isBuildable()) continue;
ModuleDependency moduleDependency = m.asDependency();
MavenModule old = modules.get(moduleDependency);
@@ -419,7 +441,7 @@ public final class MavenModule extends AbstractMavenProject getAllMavenModules() {
+ return Jenkins.getInstance().getAllItems(MavenModule.class);
+ }
+
/**
* Check if this module has dependencies recorded without a concrete version -
* which shouldn't happen for any module which was at least build once with Jenkins >= 1.207.
diff --git a/maven-plugin/src/main/java/hudson/maven/MavenModuleSet.java b/maven-plugin/src/main/java/hudson/maven/MavenModuleSet.java
index 9fc76afe208e31ca0bd1e8fa0c200e7dd646f364..02decb205efc13ce03b15717acbb5095eb12288e 100644
--- a/maven-plugin/src/main/java/hudson/maven/MavenModuleSet.java
+++ b/maven-plugin/src/main/java/hudson/maven/MavenModuleSet.java
@@ -103,7 +103,7 @@ import org.kohsuke.stapler.export.Exported;
*
* @author Kohsuke Kawaguchi
*/
-public final class MavenModuleSet extends AbstractMavenProject implements TopLevelItem, ItemGroup, SCMedItem, Saveable, BuildableItemWithBuildWrappers {
+public class MavenModuleSet extends AbstractMavenProject implements TopLevelItem, ItemGroup, SCMedItem, Saveable, BuildableItemWithBuildWrappers {
/**
* All {@link MavenModule}s, keyed by their {@link MavenModule#getModuleName()} module name}s.
*/
diff --git a/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java b/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java
index 050a5909a49b0ffea4520fc0d0474bdfeac28e25..90660fbb619cec651bb550866dfa5c29d5f52208 100644
--- a/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java
+++ b/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java
@@ -46,19 +46,28 @@ public final class ModuleDependency implements Serializable {
/**
* @since 1.395
*/
- public boolean plugin = false;
+ public final boolean plugin;
public ModuleDependency(String groupId, String artifactId, String version) {
+ this(groupId, artifactId, version, false);
+ }
+
+ public ModuleDependency(String groupId, String artifactId, String version, boolean plugin) {
this.groupId = groupId.intern();
this.artifactId = artifactId.intern();
if(version==null)
this.version = UNKNOWN;
else
this.version = version.intern();
+ this.plugin = plugin;
}
public ModuleDependency(ModuleName name, String version) {
- this(name.groupId,name.artifactId,version);
+ this(name.groupId,name.artifactId,version,false);
+ }
+
+ public ModuleDependency(ModuleName name, String version, boolean plugin) {
+ this(name.groupId,name.artifactId,version,plugin);
}
public ModuleDependency(org.apache.maven.model.Dependency dep) {
@@ -70,26 +79,25 @@ public final class ModuleDependency implements Serializable {
}
public ModuleDependency(Plugin p) {
- this(p.getGroupId(),p.getArtifactId(), Functions.defaulted(p.getVersion(),NONE));
- this.plugin = true;
+ this(p.getGroupId(),p.getArtifactId(), Functions.defaulted(p.getVersion(),NONE),true);
}
public ModuleDependency(ReportPlugin p) {
- this(p.getGroupId(),p.getArtifactId(),p.getVersion());
- this.plugin = true;
+ this(p.getGroupId(),p.getArtifactId(),p.getVersion(),true);
}
public ModuleDependency(Extension ext) {
this(ext.getGroupId(),ext.getArtifactId(),ext.getVersion());
}
- private ModuleDependency(String groupId, String artifactId) {
+ private ModuleDependency(String groupId, String artifactId, boolean plugin) {
// to be used only by the withUnknownVersion() method
// where we know that groupId and artifactId are already interned
// and where we want an UNKNOWN version
this.groupId = groupId;
this.artifactId = artifactId;
this.version = UNKNOWN;
+ this.plugin = plugin;
}
public ModuleName getName() {
@@ -103,7 +111,7 @@ public final class ModuleDependency implements Serializable {
if (UNKNOWN.equals(version))
return this;
else
- return new ModuleDependency(groupId,artifactId);
+ return new ModuleDependency(groupId,artifactId,plugin);
}
public boolean equals(Object o) {
@@ -131,7 +139,7 @@ public final class ModuleDependency implements Serializable {
* Upon reading from the disk, intern strings.
*/
public ModuleDependency readResolve() {
- return new ModuleDependency(groupId,artifactId,version);
+ return new ModuleDependency(groupId,artifactId,version,plugin);
}
/**
diff --git a/maven-plugin/src/main/java/hudson/maven/PomInfo.java b/maven-plugin/src/main/java/hudson/maven/PomInfo.java
index 8a66724d6db3448c08f94a1064989f8d8faf316e..e0c419854193e172204ffca813e7a084f2fab4d6 100644
--- a/maven-plugin/src/main/java/hudson/maven/PomInfo.java
+++ b/maven-plugin/src/main/java/hudson/maven/PomInfo.java
@@ -48,6 +48,8 @@ import java.util.ArrayList;
* @author Kohsuke Kawaguchi
*/
final class PomInfo implements Serializable {
+
+ public static final String PACKAGING_TYPE_PLUGIN = "maven-plugin";
public final ModuleName name;
@@ -107,6 +109,8 @@ final class PomInfo implements Serializable {
private final String artifactId;
public final Notifier mailNotifier;
+
+ public final String packaging;
public PomInfo(MavenProject project, PomInfo parent, String relPath) {
this.name = new ModuleName(project);
@@ -154,13 +158,14 @@ final class PomInfo implements Serializable {
this.groupId = project.getGroupId();
this.artifactId = project.getArtifactId();
+ this.packaging = project.getPackaging();
}
/**
* Creates {@link ModuleDependency} that represents this {@link PomInfo}.
*/
private ModuleDependency asDependency() {
- return new ModuleDependency(name,version);
+ return new ModuleDependency(name,version,PACKAGING_TYPE_PLUGIN.equals(this.packaging));
}
private void addPluginsAsDependencies(List plugins, Set dependencies) {
diff --git a/maven-plugin/src/test/java/hudson/maven/MavenModuleTest.java b/maven-plugin/src/test/java/hudson/maven/MavenModuleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d26bc3122686f5740f6d5a8fca2a7fe04d1bf9db
--- /dev/null
+++ b/maven-plugin/src/test/java/hudson/maven/MavenModuleTest.java
@@ -0,0 +1,130 @@
+package hudson.maven;
+
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.support.membermodification.MemberMatcher.constructor;
+import static org.powermock.api.support.membermodification.MemberModifier.suppress;
+import hudson.maven.MavenModuleSet.DescriptorImpl;
+import hudson.model.AbstractProject;
+import hudson.model.DependencyGraph;
+import hudson.model.MockHelper;
+
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.project.MavenProject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.jvnet.hudson.test.Bug;
+import org.mockito.Matchers;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.google.common.collect.Lists;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest( { MavenModuleSet.class, DescriptorImpl.class, AbstractProject.class})
+public class MavenModuleTest {
+
+ private MavenModuleSet parent;
+
+ private MavenModule module;
+
+ private MavenProject project;
+
+ @Before
+ public void before() {
+ suppress(constructor(AbstractProject.class));
+ suppress(constructor(DescriptorImpl.class));
+
+ this.module = mock(MavenModule.class);
+ basicMocking(this.module);
+
+ this.project = new MavenProject();
+ project.setGroupId("test");
+ project.setArtifactId("testmodule");
+ project.setVersion("2.0-SNAPSHOT");
+ project.setPackaging("jar");
+
+ this.module.reconfigure(new PomInfo(project, null, "relPath"));
+ this.module.doSetName("test$testmodule");
+ }
+
+ /**
+ * Tests that a {@link MavenModule} which builds a plugin is recognized as a snapshot
+ * dependency in another module using that plugin.
+ */
+ @Test
+ @Bug(10530)
+ public void testMavenModuleAsPluginDependency() {
+ MavenModule pluginModule = createPluginProject();
+
+ addModuleAsPluginDependency(this.module, pluginModule);
+
+ when(this.module.getAllMavenModules()).thenReturn(Lists.newArrayList(this.module, pluginModule));
+
+ DependencyGraph graph = MockHelper.mockDependencyGraph(
+ Lists.>newArrayList(this.module, pluginModule));
+ graph.build();
+
+ List downstream = graph.getDownstream(pluginModule);
+ Assert.assertEquals(1, downstream.size());
+ Assert.assertSame(this.module, downstream.get(0));
+ }
+
+ private static void addModuleAsPluginDependency(MavenModule module, MavenModule pluginModule) {
+ Build build = new Build();
+ Plugin plugin = new Plugin();
+ plugin.setGroupId(pluginModule.getModuleName().groupId);
+ plugin.setArtifactId(pluginModule.getModuleName().artifactId);
+ plugin.setVersion(pluginModule.getVersion());
+ build.setPlugins(Collections.singletonList(plugin));
+
+ MavenProject project = new MavenProject();
+ project.setGroupId(module.getModuleName().groupId);
+ project.setArtifactId(module.getModuleName().artifactId);
+ project.setVersion(module.getVersion());
+ project.setPackaging("jar");
+ project.setBuild(build);
+
+ module.reconfigure(new PomInfo(project, null, "relPath"));
+ }
+
+ private static MavenModule createPluginProject() {
+ MavenModule pluginModule = mock(MavenModule.class);
+ basicMocking(pluginModule);
+
+ MavenProject proj = new MavenProject();
+ proj.setGroupId("test");
+ proj.setArtifactId("pluginmodule");
+ proj.setVersion("1.0-SNAPSHOT");
+ proj.setPackaging("maven-plugin");
+ PomInfo info = new PomInfo(proj, null, "relPath");
+ pluginModule.reconfigure(info);
+ pluginModule.doSetName("test$pluginmodule");
+
+ return pluginModule;
+ }
+
+ private static void basicMocking(MavenModule mock) {
+ when(mock.isBuildable()).thenReturn(Boolean.TRUE);
+ doCallRealMethod().when(mock).reconfigure(Matchers.any(PomInfo.class));
+ doCallRealMethod().when(mock).buildDependencyGraph(Matchers.any(DependencyGraph.class));
+ when(mock.asDependency()).thenCallRealMethod();
+ doCallRealMethod().when(mock).doSetName(Matchers.anyString());
+ when(mock.getModuleName()).thenCallRealMethod();
+ when(mock.getVersion()).thenCallRealMethod();
+
+ MavenModuleSet parent = mock(MavenModuleSet.class);
+ when(parent.isAggregatorStyleBuild()).thenReturn(Boolean.FALSE);
+ when(mock.getParent()).thenReturn(parent);
+
+ when(parent.getModules()).thenReturn(Collections.singleton(mock));
+ }
+}
diff --git a/maven-plugin/src/test/java/hudson/model/MockHelper.java b/maven-plugin/src/test/java/hudson/model/MockHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c0c47793d2b8b7c291922a38fe08a06b88f116f
--- /dev/null
+++ b/maven-plugin/src/test/java/hudson/model/MockHelper.java
@@ -0,0 +1,14 @@
+package hudson.model;
+
+import java.util.Collection;
+
+import org.mockito.Mockito;
+
+public class MockHelper {
+ public static DependencyGraph mockDependencyGraph(Collection> allProjects) {
+ DependencyGraph graph = new DependencyGraph();
+ graph = Mockito.spy(graph);
+ Mockito.doReturn(allProjects).when(graph).getAllProjects();
+ return graph;
+ }
+}