Skip to content

Commit 933e203

Browse files
Introduce graphRoots for dependencyFilter based mojos (#1571)
With graphRoots it is possible to select one or more dependencies as the root of the artifact filters. By doing this you can isolate a set of jars for further purpose, like giving them their own classloader. --------- Co-authored-by: Maarten Mulders <[email protected]>
1 parent d0511ef commit 933e203

File tree

13 files changed

+592
-3
lines changed

13 files changed

+592
-3
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ under the License.
2828
</parent>
2929

3030
<artifactId>maven-dependency-plugin</artifactId>
31-
<version>3.9.1-SNAPSHOT</version>
31+
<version>3.10.0-SNAPSHOT</version>
3232
<packaging>maven-plugin</packaging>
3333

3434
<name>Apache Maven Dependency Plugin</name>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one
4+
~ or more contributor license agreements. See the NOTICE file
5+
~ distributed with this work for additional information
6+
~ regarding copyright ownership. The ASF licenses this file
7+
~ to you under the Apache License, Version 2.0 (the
8+
~ "License"); you may not use this file except in compliance
9+
~ with the License. You may obtain a copy of the License at
10+
~
11+
~ http://www.apache.org/licenses/LICENSE-2.0
12+
~
13+
~ Unless required by applicable law or agreed to in writing,
14+
~ software distributed under the License is distributed on an
15+
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
~ KIND, either express or implied. See the License for the
17+
~ specific language governing permissions and limitations
18+
~ under the License.
19+
-->
20+
21+
<project xmlns="http://maven.apache.org/POM/4.0.0"
22+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
24+
<modelVersion>4.0.0</modelVersion>
25+
26+
<groupId>org.apache.maven.its.dependencies</groupId>
27+
<artifactId>d-without-dep</artifactId>
28+
<version>3.2.1</version>
29+
30+
</project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
invoker.goals = clean process-sources
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one
4+
~ or more contributor license agreements. See the NOTICE file
5+
~ distributed with this work for additional information
6+
~ regarding copyright ownership. The ASF licenses this file
7+
~ to you under the Apache License, Version 2.0 (the
8+
~ "License"); you may not use this file except in compliance
9+
~ with the License. You may obtain a copy of the License at
10+
~
11+
~ http://www.apache.org/licenses/LICENSE-2.0
12+
~
13+
~ Unless required by applicable law or agreed to in writing,
14+
~ software distributed under the License is distributed on an
15+
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
~ KIND, either express or implied. See the License for the
17+
~ specific language governing permissions and limitations
18+
~ under the License.
19+
-->
20+
21+
<project xmlns="http://maven.apache.org/POM/4.0.0"
22+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
24+
<modelVersion>4.0.0</modelVersion>
25+
26+
<parent>
27+
<groupId>org.apache</groupId>
28+
<artifactId>apache</artifactId>
29+
<version>5</version>
30+
</parent>
31+
32+
<groupId>org.apache.maven.its.dependency</groupId>
33+
<artifactId>test</artifactId>
34+
<version>1.0-SNAPSHOT</version>
35+
36+
<name>Test</name>
37+
<description>
38+
Test dependency:copy-dependencies using graphRoots
39+
</description>
40+
41+
<dependencies>
42+
<dependency>
43+
<groupId>org.apache.maven.its.dependencies</groupId>
44+
<artifactId>d-without-dep</artifactId>
45+
<version>3.2.1</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.apache.maven.its.dependency</groupId>
49+
<artifactId>a-with-dep</artifactId>
50+
<version>1.0.0</version>
51+
<scope>provided</scope>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.apache.maven.its.dependency</groupId>
55+
<artifactId>get-artifact</artifactId>
56+
<version>1.0</version>
57+
<scope>provided</scope>
58+
</dependency>
59+
</dependencies>
60+
61+
<properties>
62+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
63+
</properties>
64+
65+
<build>
66+
67+
<defaultGoal>package</defaultGoal>
68+
69+
<plugins>
70+
<plugin>
71+
<artifactId>maven-dependency-plugin</artifactId>
72+
<version>@project.version@</version>
73+
<executions>
74+
<execution>
75+
<id>test-1</id>
76+
<goals>
77+
<goal>copy-dependencies</goal>
78+
</goals>
79+
<configuration>
80+
<graphRoots>
81+
<graphRoot>
82+
<groupId>org.apache.maven.its.dependency</groupId>
83+
<artifactId>a-with-dep</artifactId>
84+
</graphRoot>
85+
<graphRoot>
86+
<groupId>org.apache.maven.its.dependencies</groupId>
87+
<artifactId>d-without-dep</artifactId>
88+
</graphRoot>
89+
</graphRoots>
90+
<outputDirectory>${project.build.directory}/it/copy-dep-test-1</outputDirectory>
91+
</configuration>
92+
</execution>
93+
<execution>
94+
<id>test-2</id>
95+
<goals>
96+
<goal>copy-dependencies</goal>
97+
</goals>
98+
<configuration>
99+
<graphRoots>
100+
<graphRoot>
101+
<groupId>org.apache.maven.its.dependency</groupId>
102+
<artifactId>get-artifact</artifactId>
103+
</graphRoot>
104+
</graphRoots>
105+
<outputDirectory>${project.build.directory}/it/copy-dep-test-2</outputDirectory>
106+
</configuration>
107+
</execution>
108+
</executions>
109+
</plugin>
110+
</plugins>
111+
</build>
112+
</project>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
def targetFiles1 = ['a-with-dep-1.0.0.jar', 'b-with-dep-1.0.0.jar', 'c-without-dep-1.0.0.jar', 'd-without-dep-3.2.1.jar']
21+
def directory1 = new File(basedir, 'target/it/copy-dep-test-1')
22+
23+
// Get only file names from the directory (excludes subdirectories)
24+
def actualFiles1 = directory1.listFiles().findAll { it.isFile() }.collect { it.name }
25+
26+
// Check if the sets are identical and have exactly 3 files
27+
assert (actualFiles1.size() == 4 && actualFiles1.containsAll(targetFiles1))
28+
29+
30+
def targetFiles2 = ['get-artifact-1.0.jar', 'get-artifact-transitive-1.0.jar']
31+
def directory2 = new File(basedir, 'target/it/copy-dep-test-2')
32+
33+
// Get only file names from the directory (excludes subdirectories)
34+
def actualFiles2 = directory2.listFiles().findAll { it.isFile() }.collect { it.name }
35+
36+
// Check if the sets are identical and have exactly 3 files
37+
assert (actualFiles2.size() == 2 && actualFiles2.containsAll(targetFiles2))

src/main/java/org/apache/maven/plugins/dependency/fromDependencies/AbstractDependencyFilterMojo.java

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@
2323
import java.io.File;
2424
import java.util.ArrayList;
2525
import java.util.Collection;
26+
import java.util.HashSet;
2627
import java.util.LinkedHashSet;
28+
import java.util.List;
2729
import java.util.Set;
30+
import java.util.stream.Collectors;
2831

2932
import org.apache.maven.RepositoryUtils;
3033
import org.apache.maven.artifact.Artifact;
3134
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
3235
import org.apache.maven.execution.MavenSession;
36+
import org.apache.maven.model.Dependency;
3337
import org.apache.maven.plugin.MojoExecutionException;
3438
import org.apache.maven.plugins.annotations.Parameter;
3539
import org.apache.maven.plugins.dependency.AbstractDependencyMojo;
@@ -52,7 +56,9 @@
5256
import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter;
5357
import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
5458
import org.apache.maven.shared.artifact.filter.collection.TypeFilter;
59+
import org.eclipse.aether.repository.RemoteRepository;
5560
import org.eclipse.aether.resolution.ArtifactResolutionException;
61+
import org.eclipse.aether.resolution.DependencyResolutionException;
5662
import org.sonatype.plexus.build.incremental.BuildContext;
5763

5864
/**
@@ -231,6 +237,16 @@ public abstract class AbstractDependencyFilterMojo extends AbstractDependencyMoj
231237
@Parameter(property = "mdep.prependGroupId", defaultValue = "false")
232238
protected boolean prependGroupId = false;
233239

240+
/**
241+
* By default, this goal uses the project itself as the root of the dependency tree.
242+
* With graphRoots, you can select a subtree of dependencies based on groupId and artifactId.
243+
* After that, the general include/exclude filters can be applied.
244+
*
245+
* @since 3.10.0
246+
*/
247+
@Parameter
248+
private List<GraphRoot> graphRoots;
249+
234250
private final ResolverUtil resolverUtil;
235251

236252
private final ProjectBuilder projectBuilder;
@@ -292,6 +308,7 @@ protected DependencyStatusSets getDependencySets(boolean stopOnFailure) throws M
292308
*/
293309
protected DependencyStatusSets getDependencySets(boolean stopOnFailure, boolean includeParents)
294310
throws MojoExecutionException {
311+
295312
// add filters in well known order, least specific to most specific
296313
FilterArtifacts filter = new FilterArtifacts();
297314

@@ -323,7 +340,13 @@ protected DependencyStatusSets getDependencySets(boolean stopOnFailure, boolean
323340
DependencyUtil.cleanToBeTokenizedString(this.excludeArtifactIds)));
324341

325342
// start with all artifacts.
326-
Set<Artifact> artifacts = getProject().getArtifacts();
343+
Set<Artifact> artifacts;
344+
345+
try {
346+
artifacts = collectArtifacts(getProject());
347+
} catch (DependencyResolutionException e) {
348+
throw new MojoExecutionException("Failed to collect artifacts", e);
349+
}
327350

328351
if (includeParents) {
329352
// add dependencies parents
@@ -479,6 +502,41 @@ private Set<Artifact> resolve(Set<org.eclipse.aether.artifact.Artifact> artifact
479502
return resolvedArtifacts;
480503
}
481504

505+
private Set<Artifact> collectArtifacts(MavenProject project) throws DependencyResolutionException {
506+
if (graphRoots == null || graphRoots.isEmpty()) {
507+
// artifact have already been resolved here due to
508+
// @Mojo(requiresDependencyResolution = ResolutionScope.TEST) on final Mojo
509+
return project.getArtifacts();
510+
} else {
511+
// MavenProject doesn't provide access to the graph of dependencies(only the direct dependencies)
512+
// Hence we need to re-resolve artifacts, but only for the matching graphnodes
513+
List<DependencyMatcher> filterMatchers =
514+
graphRoots.stream().map(GraphRootMatcher::new).collect(Collectors.toList());
515+
516+
DependencyMatcher subTreeMatcher = new OrDependencyMatcher(filterMatchers);
517+
518+
Set<Artifact> artifacts = new HashSet<>();
519+
for (Dependency dep : project.getDependencies()) {
520+
if (subTreeMatcher.matches(dep)) {
521+
artifacts.addAll(resolveDependencyArtifacts(dep));
522+
}
523+
}
524+
return artifacts;
525+
}
526+
}
527+
528+
private Set<Artifact> resolveDependencyArtifacts(Dependency root) throws DependencyResolutionException {
529+
org.eclipse.aether.graph.Dependency dependency = RepositoryUtils.toDependency(
530+
root, session.getRepositorySession().getArtifactTypeRegistry());
531+
532+
List<RemoteRepository> remoteRepositories = getProject().getRemoteProjectRepositories();
533+
534+
Collection<org.eclipse.aether.artifact.Artifact> depArtifacts =
535+
resolverUtil.resolveDependencies(dependency.getArtifact(), remoteRepositories);
536+
537+
return depArtifacts.stream().map(RepositoryUtils::toArtifact).collect(Collectors.toSet());
538+
}
539+
482540
/**
483541
* @return returns the markersDirectory
484542
*/
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.plugins.dependency.fromDependencies;
20+
21+
import org.apache.maven.model.Dependency;
22+
23+
@FunctionalInterface
24+
public interface DependencyMatcher {
25+
26+
boolean matches(Dependency dependency);
27+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.plugins.dependency.fromDependencies;
20+
21+
public class GraphRoot {
22+
23+
private String groupId;
24+
25+
private String artifactId;
26+
27+
public String getGroupId() {
28+
return groupId;
29+
}
30+
31+
public void setGroupId(String groupId) {
32+
this.groupId = groupId;
33+
}
34+
35+
public String getArtifactId() {
36+
return artifactId;
37+
}
38+
39+
public void setArtifactId(String artifactId) {
40+
this.artifactId = artifactId;
41+
}
42+
}

0 commit comments

Comments
 (0)