Apply exclusions to existing war entries

Update `RepackageMojo` and supporting classes so that `exclusions`
on the repackage goal apply to both the contributed libraries and any
existing jar entries already contained in the original war.

Prior to this commit, exclusions would apply to contributed jars (for
example, those in `WEB-INF/lib-provided`) but not jars that were
packaged directly into `WEB-INF/lib` by the war plugin

Fixes gh-15808

Co-authored-by: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
Madhura Bhave
2021-06-08 17:23:04 -07:00
committed by Phillip Webb
parent 4d694ddaa8
commit b790c82732
17 changed files with 346 additions and 81 deletions

View File

@@ -89,4 +89,13 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests {
});
}
@TestTemplate
void whenEntryIsExcludedItShouldNotBePresentInTheRepackagedWar(MavenBuild mavenBuild) {
mavenBuild.project("war-exclude-entry").execute((project) -> {
File war = new File(project, "target/war-exclude-entry-0.0.1.BUILD-SNAPSHOT.war");
assertThat(jar(war)).hasEntryWithNameStartingWith("WEB-INF/lib/spring-context")
.doesNotHaveEntryWithNameStartingWith("WEB-INF/lib/spring-core");
});
}
}

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>war-exclude-entry</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>@java.version@</maven.compiler.source>
<maven.compiler.target>@java.version@</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>@maven-war-plugin.version@</version>
<configuration>
<archive>
<manifestEntries>
<Not-Used>Foo</Not-Used>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>@spring-framework.version@</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>@jakarta-servlet.version@</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed 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
*
* https://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.
*/
package org.test;
public class SampleApplication {
public static void main(String[] args) {
}
}

View File

@@ -76,7 +76,7 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo {
this.excludeGroupIds = excludeGroupIds;
}
protected Set<Artifact> filterDependencies(Set<Artifact> dependencies, FilterArtifacts filters)
protected final Set<Artifact> filterDependencies(Set<Artifact> dependencies, FilterArtifacts filters)
throws MojoExecutionException {
try {
Set<Artifact> filtered = new LinkedHashSet<>(dependencies);

View File

@@ -182,8 +182,9 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
* @throws MojoExecutionException on execution error
*/
protected final Libraries getLibraries(Collection<Dependency> unpacks) throws MojoExecutionException {
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), getFilters(getAdditionalFilters()));
return new ArtifactsLibraries(artifacts, this.session.getProjects(), unpacks, getLog());
Set<Artifact> artifacts = this.project.getArtifacts();
Set<Artifact> includedArtifacts = filterDependencies(artifacts, getFilters(getAdditionalFilters()));
return new ArtifactsLibraries(artifacts, includedArtifacts, this.session.getProjects(), unpacks, getLog());
}
private ArtifactsFilter[] getAdditionalFilters() {

View File

@@ -16,6 +16,7 @@
package org.springframework.boot.maven;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
@@ -59,6 +60,8 @@ public class ArtifactsLibraries implements Libraries {
private final Set<Artifact> artifacts;
private final Set<Artifact> includedArtifacts;
private final Collection<MavenProject> localProjects;
private final Collection<Dependency> unpacks;
@@ -89,7 +92,23 @@ public class ArtifactsLibraries implements Libraries {
*/
public ArtifactsLibraries(Set<Artifact> artifacts, Collection<MavenProject> localProjects,
Collection<Dependency> unpacks, Log log) {
this(artifacts, artifacts, localProjects, unpacks, log);
}
/**
* Creates a new {@code ArtifactsLibraries} from the given {@code artifacts}.
* @param artifacts all artifacts that can be represented as libraries
* @param includedArtifacts the actual artifacts to include in the fat jar
* @param localProjects projects for which {@link Library#isLocal() local} libraries
* should be created
* @param unpacks artifacts that should be unpacked on launch
* @param log the log
* @since 2.4.8
*/
public ArtifactsLibraries(Set<Artifact> artifacts, Set<Artifact> includedArtifacts,
Collection<MavenProject> localProjects, Collection<Dependency> unpacks, Log log) {
this.artifacts = artifacts;
this.includedArtifacts = includedArtifacts;
this.localProjects = localProjects;
this.unpacks = unpacks;
this.log = log;
@@ -99,18 +118,22 @@ public class ArtifactsLibraries implements Libraries {
public void doWithLibraries(LibraryCallback callback) throws IOException {
Set<String> duplicates = getDuplicates(this.artifacts);
for (Artifact artifact : this.artifacts) {
String name = getFileName(artifact);
File file = artifact.getFile();
LibraryScope scope = SCOPES.get(artifact.getScope());
if (scope != null && artifact.getFile() != null) {
String name = getFileName(artifact);
if (duplicates.contains(name)) {
this.log.debug("Duplicate found: " + name);
name = artifact.getGroupId() + "-" + name;
this.log.debug("Renamed to: " + name);
}
LibraryCoordinates coordinates = new ArtifactLibraryCoordinates(artifact);
callback.library(new Library(name, artifact.getFile(), scope, coordinates, isUnpackRequired(artifact),
isLocal(artifact)));
if (scope == null || file == null) {
continue;
}
if (duplicates.contains(name)) {
this.log.debug("Duplicate found: " + name);
name = artifact.getGroupId() + "-" + name;
this.log.debug("Renamed to: " + name);
}
LibraryCoordinates coordinates = new ArtifactLibraryCoordinates(artifact);
boolean unpackRequired = isUnpackRequired(artifact);
boolean local = isLocal(artifact);
boolean included = this.includedArtifacts.contains(artifact);
callback.library(new Library(name, file, scope, coordinates, unpackRequired, local, included));
}
}

View File

@@ -189,8 +189,8 @@ public class BuildImageMojo extends AbstractPackagerMojo {
}
/**
* Return the layout factory that will be used to determine the {@link LayoutType} if
* no explicit layout is set.
* Return the layout factory that will be used to determine the
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
* @return the value of the {@code layoutFactory} parameter, or {@code null} if the
* parameter is not provided
*/

View File

@@ -183,8 +183,8 @@ public class RepackageMojo extends AbstractPackagerMojo {
}
/**
* Return the layout factory that will be used to determine the {@link LayoutType} if
* no explicit layout is set.
* Return the layout factory that will be used to determine the
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
* @return the value of the {@code layoutFactory} parameter, or {@code null} if the
* parameter is not provided
*/

View File

@@ -144,6 +144,7 @@ class ArtifactsLibrariesTests {
this.artifacts = Collections.singleton(snapshotArtifact);
new ArtifactsLibraries(this.artifacts, Collections.emptyList(), null, mock(Log.class))
.doWithLibraries((library) -> {
assertThat(library.isIncluded()).isTrue();
assertThat(library.isLocal()).isFalse();
assertThat(library.getCoordinates().getVersion()).isEqualTo("1.0-SNAPSHOT");
});
@@ -181,4 +182,19 @@ class ArtifactsLibrariesTests {
.doWithLibraries((library) -> assertThat(library.isLocal()).isTrue());
}
@Test
void nonIncludedArtifact() throws IOException {
Artifact artifact = mock(Artifact.class);
given(artifact.getScope()).willReturn("compile");
given(artifact.getArtifactId()).willReturn("artifact");
given(artifact.getBaseVersion()).willReturn("1.0-SNAPSHOT");
given(artifact.getFile()).willReturn(new File("a"));
given(artifact.getArtifactHandler()).willReturn(this.artifactHandler);
MavenProject mavenProject = mock(MavenProject.class);
given(mavenProject.getArtifact()).willReturn(artifact);
this.artifacts = Collections.singleton(artifact);
new ArtifactsLibraries(this.artifacts, Collections.emptySet(), Collections.singleton(mavenProject), null,
mock(Log.class)).doWithLibraries((library) -> assertThat(library.isIncluded()).isFalse());
}
}