Skip to content

Commit

Permalink
[MSHADE-478] Extra JARs feature (#228)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
cstamas committed May 28, 2024
1 parent 199ffae commit b573b8c
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 16 deletions.
18 changes: 18 additions & 0 deletions src/it/projects/extrajar-missing-file/invoker.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

invoker.buildResult = failure
71 changes: 71 additions & 0 deletions src/it/projects/extrajar-missing-file/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

<project>
<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.maven.its.shade.extrajar</groupId>
<artifactId>test</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

<name>extrajar</name>
<description>
Test that asserts failure if specified extra jar is not a file.
</description>

<properties>
<slf4j.version>1.7.36</slf4j.version>
</properties>

<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<id>attach-shade</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<extraJars>
<extraJar>${project.build.directory}/no-such-file.jar</extraJar>
</extraJars>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
79 changes: 79 additions & 0 deletions src/it/projects/extrajar/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

<project>
<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.maven.its.shade.extrajar</groupId>
<artifactId>test</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

<name>extrajar</name>
<description>
Test that asserts addition of extra-jars.
</description>

<properties>
<slf4j.version>1.7.36</slf4j.version>
</properties>

<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<id>attach-shade</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<extraArtifacts>
<extraArtifact>org.slf4j:slf4j-simple:${slf4j.version}</extraArtifact>
</extraArtifacts>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
39 changes: 39 additions & 0 deletions src/it/projects/extrajar/verify.bsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import java.io.*;
import java.util.jar.*;

String[] wanted =
{
"org/slf4j/Logger.class",
"org/slf4j/impl/SimpleLogger.class"
};

JarFile jarFile = new JarFile( new File( basedir, "target/test-1.0-shaded.jar" ) );

for ( String path : wanted )
{
if ( jarFile.getEntry( path ) == null )
{
throw new IllegalStateException( "wanted path is missing: " + path );
}
}

jarFile.close();
100 changes: 84 additions & 16 deletions src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;

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

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

/**
* Extra JAR files to infuse into shaded result. Accepts list of files that must exists. If any of specified
* files does not exist (or is not a file), Mojo will fail.
* <p>
* Extra JARs will be processed in same way as main JAR (if any) is: applied relocation, resource transformers
* but <em>not filtering</em>.
* <p>
* Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more
* just a feature to be able to slightly "differentiate" shaded JAR from main only.
*
* @since 3.6.0
*/
@Parameter
private List<File> extraJars;

/**
* Extra Artifacts to infuse into shaded result. Accepts list of GAVs in form of
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>} that will be resolved. If any of them
* cannot be resolved, Mojo will fail.
* <p>
* The artifacts will be resolved (not transitively), and will be processed in same way as dependency JARs
* are (if any): applied relocation, resource transformers and filtering.
* <p>
* Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more
* just a feature to be able to slightly "differentiate" shaded JAR from main only.
*
* @since 3.6.0
*/
@Parameter
private List<String> extraArtifacts;

@Inject
private MavenProjectHelper projectHelper;

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

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

if (extraJars != null && !extraJars.isEmpty()) {
for (File extraJar : extraJars) {
if (!Files.isRegularFile(extraJar.toPath())) {
throw new MojoExecutionException(
"Failed to create shaded artifact: parameter extraJars contains path " + extraJar
+ " that is not a file (does not exist or is not a file)");
}
artifacts.add(extraJar);
}
}

if (createSourcesJar) {
File file = shadedSourcesArtifactFile();
if (file.isFile()) {
Expand Down Expand Up @@ -680,15 +721,40 @@ private void processArtifactSelectors(
Set<File> sourceArtifacts,
Set<File> testArtifacts,
Set<File> testSourceArtifacts,
ArtifactSelector artifactSelector) {
ArtifactSelector artifactSelector)
throws MojoExecutionException {

List<String> excludedArtifacts = new ArrayList<>();
List<String> pomArtifacts = new ArrayList<>();
List<String> emptySourceArtifacts = new ArrayList<>();
List<String> emptyTestArtifacts = new ArrayList<>();
List<String> emptyTestSourceArtifacts = new ArrayList<>();

for (Artifact artifact : project.getArtifacts()) {
ArrayList<Artifact> processedArtifacts = new ArrayList<>();
if (extraArtifacts != null && !extraArtifacts.isEmpty()) {
processedArtifacts.addAll(extraArtifacts.stream()
.map(org.eclipse.aether.artifact.DefaultArtifact::new)
.map(RepositoryUtils::toArtifact)
.collect(Collectors.toList()));

for (Artifact artifact : processedArtifacts) {
try {
org.eclipse.aether.artifact.Artifact resolved =
resolveArtifact(RepositoryUtils.toArtifact(artifact));
if (resolved.getFile() != null) {
artifact.setFile(resolved.getFile());
}
} catch (ArtifactResolutionException e) {
throw new MojoExecutionException(
"Failed to create shaded artifact: parameter extraArtifacts contains artifact "
+ artifact.getId() + " that is not resolvable",
e);
}
}
}
processedArtifacts.addAll(project.getArtifacts());

for (Artifact artifact : processedArtifacts) {
if (!artifactSelector.isSelected(artifact)) {
excludedArtifacts.add(artifact.getId());

Expand Down Expand Up @@ -802,7 +868,7 @@ private void copyFiles(File source, File target) throws IOException {
}

private File resolveArtifactForClassifier(Artifact artifact, String classifier) {
org.eclipse.aether.artifact.Artifact coordinate = RepositoryUtils.toArtifact(new DefaultArtifact(
Artifact toResolve = new DefaultArtifact(
artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersionRange() == null
Expand All @@ -812,24 +878,26 @@ private File resolveArtifactForClassifier(Artifact artifact, String classifier)
artifact.getType(),
classifier,
artifact.getArtifactHandler(),
artifact.isOptional()));

ArtifactRequest request = new ArtifactRequest(
coordinate, RepositoryUtils.toRepos(project.getRemoteArtifactRepositories()), "shade");

Artifact resolvedArtifact;
artifact.isOptional());
try {
ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
resolvedArtifact = RepositoryUtils.toArtifact(result.getArtifact());
org.eclipse.aether.artifact.Artifact resolved = resolveArtifact(RepositoryUtils.toArtifact(toResolve));
if (resolved.getFile() != null) {
return resolved.getFile();
}
return null;
} catch (ArtifactResolutionException e) {
getLog().warn("Could not get " + classifier + " for " + artifact);
return null;
}
}

if (resolvedArtifact.isResolved()) {
return resolvedArtifact.getFile();
}
return null;
private org.eclipse.aether.artifact.Artifact resolveArtifact(org.eclipse.aether.artifact.Artifact artifact)
throws ArtifactResolutionException {
return repositorySystem
.resolveArtifact(
session.getRepositorySession(),
new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), "shade"))
.getArtifact();
}

private List<Relocator> getRelocators() {
Expand Down

0 comments on commit b573b8c

Please sign in to comment.