Skip to content

Commit b573b8c

Browse files
authored
[MSHADE-478] Extra JARs feature (#228)
Ability to add "extra JARs and Artifacts" and still enjoy the full benefits of relocation, resource transformation, etc. --- https://issues.apache.org/jira/browse/MSHADE-478
1 parent 199ffae commit b573b8c

File tree

5 files changed

+291
-16
lines changed

5 files changed

+291
-16
lines changed
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.buildResult = failure
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
-->
21+
22+
<project>
23+
<modelVersion>4.0.0</modelVersion>
24+
25+
<groupId>org.apache.maven.its.shade.extrajar</groupId>
26+
<artifactId>test</artifactId>
27+
<version>1.0</version>
28+
<packaging>jar</packaging>
29+
30+
<name>extrajar</name>
31+
<description>
32+
Test that asserts failure if specified extra jar is not a file.
33+
</description>
34+
35+
<properties>
36+
<slf4j.version>1.7.36</slf4j.version>
37+
</properties>
38+
39+
<dependencies>
40+
<dependency>
41+
<groupId>org.slf4j</groupId>
42+
<artifactId>slf4j-api</artifactId>
43+
<version>${slf4j.version}</version>
44+
</dependency>
45+
</dependencies>
46+
47+
<build>
48+
<plugins>
49+
<plugin>
50+
<groupId>org.apache.maven.plugins</groupId>
51+
<artifactId>maven-shade-plugin</artifactId>
52+
<version>@project.version@</version>
53+
<executions>
54+
<execution>
55+
<id>attach-shade</id>
56+
<phase>package</phase>
57+
<goals>
58+
<goal>shade</goal>
59+
</goals>
60+
<configuration>
61+
<shadedArtifactAttached>true</shadedArtifactAttached>
62+
<extraJars>
63+
<extraJar>${project.build.directory}/no-such-file.jar</extraJar>
64+
</extraJars>
65+
</configuration>
66+
</execution>
67+
</executions>
68+
</plugin>
69+
</plugins>
70+
</build>
71+
</project>

src/it/projects/extrajar/pom.xml

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
-->
21+
22+
<project>
23+
<modelVersion>4.0.0</modelVersion>
24+
25+
<groupId>org.apache.maven.its.shade.extrajar</groupId>
26+
<artifactId>test</artifactId>
27+
<version>1.0</version>
28+
<packaging>jar</packaging>
29+
30+
<name>extrajar</name>
31+
<description>
32+
Test that asserts addition of extra-jars.
33+
</description>
34+
35+
<properties>
36+
<slf4j.version>1.7.36</slf4j.version>
37+
</properties>
38+
39+
<dependencies>
40+
<dependency>
41+
<groupId>org.slf4j</groupId>
42+
<artifactId>slf4j-api</artifactId>
43+
<version>${slf4j.version}</version>
44+
</dependency>
45+
</dependencies>
46+
47+
<build>
48+
<plugins>
49+
<plugin>
50+
<groupId>org.apache.maven.plugins</groupId>
51+
<artifactId>maven-shade-plugin</artifactId>
52+
<version>@project.version@</version>
53+
<executions>
54+
<execution>
55+
<id>attach-shade</id>
56+
<phase>package</phase>
57+
<goals>
58+
<goal>shade</goal>
59+
</goals>
60+
<configuration>
61+
<shadedArtifactAttached>true</shadedArtifactAttached>
62+
<extraArtifacts>
63+
<extraArtifact>org.slf4j:slf4j-simple:${slf4j.version}</extraArtifact>
64+
</extraArtifacts>
65+
<filters>
66+
<filter>
67+
<artifact>*:*</artifact>
68+
<excludes>
69+
<exclude>META-INF/MANIFEST.MF</exclude>
70+
</excludes>
71+
</filter>
72+
</filters>
73+
</configuration>
74+
</execution>
75+
</executions>
76+
</plugin>
77+
</plugins>
78+
</build>
79+
</project>

src/it/projects/extrajar/verify.bsh

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
import java.io.*;
21+
import java.util.jar.*;
22+
23+
String[] wanted =
24+
{
25+
"org/slf4j/Logger.class",
26+
"org/slf4j/impl/SimpleLogger.class"
27+
};
28+
29+
JarFile jarFile = new JarFile( new File( basedir, "target/test-1.0-shaded.jar" ) );
30+
31+
for ( String path : wanted )
32+
{
33+
if ( jarFile.getEntry( path ) == null )
34+
{
35+
throw new IllegalStateException( "wanted path is missing: " + path );
36+
}
37+
}
38+
39+
jarFile.close();

src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java

+84-16
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@
7777
import org.eclipse.aether.graph.DependencyNode;
7878
import org.eclipse.aether.resolution.ArtifactRequest;
7979
import org.eclipse.aether.resolution.ArtifactResolutionException;
80-
import org.eclipse.aether.resolution.ArtifactResult;
8180

8281
import static org.apache.maven.plugins.shade.resource.UseDependencyReducedPom.createPomReplaceTransformers;
8382

@@ -393,6 +392,37 @@ public class ShadeMojo extends AbstractMojo {
393392
@Parameter(defaultValue = "false")
394393
private boolean skip;
395394

395+
/**
396+
* Extra JAR files to infuse into shaded result. Accepts list of files that must exists. If any of specified
397+
* files does not exist (or is not a file), Mojo will fail.
398+
* <p>
399+
* Extra JARs will be processed in same way as main JAR (if any) is: applied relocation, resource transformers
400+
* but <em>not filtering</em>.
401+
* <p>
402+
* Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more
403+
* just a feature to be able to slightly "differentiate" shaded JAR from main only.
404+
*
405+
* @since 3.6.0
406+
*/
407+
@Parameter
408+
private List<File> extraJars;
409+
410+
/**
411+
* Extra Artifacts to infuse into shaded result. Accepts list of GAVs in form of
412+
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>} that will be resolved. If any of them
413+
* cannot be resolved, Mojo will fail.
414+
* <p>
415+
* The artifacts will be resolved (not transitively), and will be processed in same way as dependency JARs
416+
* are (if any): applied relocation, resource transformers and filtering.
417+
* <p>
418+
* Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more
419+
* just a feature to be able to slightly "differentiate" shaded JAR from main only.
420+
*
421+
* @since 3.6.0
422+
*/
423+
@Parameter
424+
private List<String> extraArtifacts;
425+
396426
@Inject
397427
private MavenProjectHelper projectHelper;
398428

@@ -444,6 +474,17 @@ public void execute() throws MojoExecutionException {
444474

445475
artifacts.add(project.getArtifact().getFile());
446476

477+
if (extraJars != null && !extraJars.isEmpty()) {
478+
for (File extraJar : extraJars) {
479+
if (!Files.isRegularFile(extraJar.toPath())) {
480+
throw new MojoExecutionException(
481+
"Failed to create shaded artifact: parameter extraJars contains path " + extraJar
482+
+ " that is not a file (does not exist or is not a file)");
483+
}
484+
artifacts.add(extraJar);
485+
}
486+
}
487+
447488
if (createSourcesJar) {
448489
File file = shadedSourcesArtifactFile();
449490
if (file.isFile()) {
@@ -680,15 +721,40 @@ private void processArtifactSelectors(
680721
Set<File> sourceArtifacts,
681722
Set<File> testArtifacts,
682723
Set<File> testSourceArtifacts,
683-
ArtifactSelector artifactSelector) {
724+
ArtifactSelector artifactSelector)
725+
throws MojoExecutionException {
684726

685727
List<String> excludedArtifacts = new ArrayList<>();
686728
List<String> pomArtifacts = new ArrayList<>();
687729
List<String> emptySourceArtifacts = new ArrayList<>();
688730
List<String> emptyTestArtifacts = new ArrayList<>();
689731
List<String> emptyTestSourceArtifacts = new ArrayList<>();
690732

691-
for (Artifact artifact : project.getArtifacts()) {
733+
ArrayList<Artifact> processedArtifacts = new ArrayList<>();
734+
if (extraArtifacts != null && !extraArtifacts.isEmpty()) {
735+
processedArtifacts.addAll(extraArtifacts.stream()
736+
.map(org.eclipse.aether.artifact.DefaultArtifact::new)
737+
.map(RepositoryUtils::toArtifact)
738+
.collect(Collectors.toList()));
739+
740+
for (Artifact artifact : processedArtifacts) {
741+
try {
742+
org.eclipse.aether.artifact.Artifact resolved =
743+
resolveArtifact(RepositoryUtils.toArtifact(artifact));
744+
if (resolved.getFile() != null) {
745+
artifact.setFile(resolved.getFile());
746+
}
747+
} catch (ArtifactResolutionException e) {
748+
throw new MojoExecutionException(
749+
"Failed to create shaded artifact: parameter extraArtifacts contains artifact "
750+
+ artifact.getId() + " that is not resolvable",
751+
e);
752+
}
753+
}
754+
}
755+
processedArtifacts.addAll(project.getArtifacts());
756+
757+
for (Artifact artifact : processedArtifacts) {
692758
if (!artifactSelector.isSelected(artifact)) {
693759
excludedArtifacts.add(artifact.getId());
694760

@@ -802,7 +868,7 @@ private void copyFiles(File source, File target) throws IOException {
802868
}
803869

804870
private File resolveArtifactForClassifier(Artifact artifact, String classifier) {
805-
org.eclipse.aether.artifact.Artifact coordinate = RepositoryUtils.toArtifact(new DefaultArtifact(
871+
Artifact toResolve = new DefaultArtifact(
806872
artifact.getGroupId(),
807873
artifact.getArtifactId(),
808874
artifact.getVersionRange() == null
@@ -812,24 +878,26 @@ private File resolveArtifactForClassifier(Artifact artifact, String classifier)
812878
artifact.getType(),
813879
classifier,
814880
artifact.getArtifactHandler(),
815-
artifact.isOptional()));
816-
817-
ArtifactRequest request = new ArtifactRequest(
818-
coordinate, RepositoryUtils.toRepos(project.getRemoteArtifactRepositories()), "shade");
819-
820-
Artifact resolvedArtifact;
881+
artifact.isOptional());
821882
try {
822-
ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
823-
resolvedArtifact = RepositoryUtils.toArtifact(result.getArtifact());
883+
org.eclipse.aether.artifact.Artifact resolved = resolveArtifact(RepositoryUtils.toArtifact(toResolve));
884+
if (resolved.getFile() != null) {
885+
return resolved.getFile();
886+
}
887+
return null;
824888
} catch (ArtifactResolutionException e) {
825889
getLog().warn("Could not get " + classifier + " for " + artifact);
826890
return null;
827891
}
892+
}
828893

829-
if (resolvedArtifact.isResolved()) {
830-
return resolvedArtifact.getFile();
831-
}
832-
return null;
894+
private org.eclipse.aether.artifact.Artifact resolveArtifact(org.eclipse.aether.artifact.Artifact artifact)
895+
throws ArtifactResolutionException {
896+
return repositorySystem
897+
.resolveArtifact(
898+
session.getRepositorySession(),
899+
new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), "shade"))
900+
.getArtifact();
833901
}
834902

835903
private List<Relocator> getRelocators() {

0 commit comments

Comments
 (0)