Commit b790c827 authored by Madhura Bhave's avatar Madhura Bhave Committed by Phillip Webb

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: 's avatarPhillip Webb <pwebb@vmware.com>
parent 4d694dda
...@@ -198,7 +198,7 @@ abstract class ArchiveCommand extends OptionParsingCommand { ...@@ -198,7 +198,7 @@ abstract class ArchiveCommand extends OptionParsingCommand {
List<Library> libraries = new ArrayList<>(); List<Library> libraries = new ArrayList<>();
for (URL dependency : dependencies) { for (URL dependency : dependencies) {
File file = new File(dependency.toURI()); File file = new File(dependency.toURI());
libraries.add(new Library(file, getLibraryScope(file))); libraries.add(new Library(null, file, getLibraryScope(file), null, false, false, true));
} }
return libraries; return libraries;
} }
...@@ -256,7 +256,7 @@ abstract class ArchiveCommand extends OptionParsingCommand { ...@@ -256,7 +256,7 @@ abstract class ArchiveCommand extends OptionParsingCommand {
List<Library> libraries = new ArrayList<>(); List<Library> libraries = new ArrayList<>();
for (MatchedResource entry : entries) { for (MatchedResource entry : entries) {
if (entry.isRoot()) { if (entry.isRoot()) {
libraries.add(new Library(entry.getFile(), LibraryScope.COMPILE)); libraries.add(new Library(null, entry.getFile(), LibraryScope.COMPILE, null, false, false, true));
} }
else { else {
writeClasspathEntry(writer, entry); writeClasspathEntry(writer, entry);
......
...@@ -24,6 +24,7 @@ import org.gradle.api.specs.Spec; ...@@ -24,6 +24,7 @@ import org.gradle.api.specs.Spec;
import org.springframework.boot.gradle.tasks.bundling.ResolvedDependencies.DependencyDescriptor; import org.springframework.boot.gradle.tasks.bundling.ResolvedDependencies.DependencyDescriptor;
import org.springframework.boot.loader.tools.Layer; import org.springframework.boot.loader.tools.Layer;
import org.springframework.boot.loader.tools.Library; import org.springframework.boot.loader.tools.Library;
import org.springframework.boot.loader.tools.LibraryCoordinates;
/** /**
* Resolver backed by a {@link LayeredSpec} that provides the destination {@link Layer} * Resolver backed by a {@link LayeredSpec} that provides the destination {@link Layer}
...@@ -77,9 +78,12 @@ class LayerResolver { ...@@ -77,9 +78,12 @@ class LayerResolver {
private Library asLibrary(FileCopyDetails details) { private Library asLibrary(FileCopyDetails details) {
File file = details.getFile(); File file = details.getFile();
DependencyDescriptor dependency = this.resolvedDependencies.find(file); DependencyDescriptor dependency = this.resolvedDependencies.find(file);
return (dependency != null) if (dependency == null) {
? new Library(null, file, null, dependency.getCoordinates(), false, dependency.isProjectDependency()) return new Library(null, file, null, null, false, false, true);
: new Library(file, null); }
LibraryCoordinates coordinates = dependency.getCoordinates();
boolean projectDependency = dependency.isProjectDependency();
return new Library(null, file, null, coordinates, false, projectDependency, true);
} }
} }
...@@ -30,6 +30,7 @@ import java.util.Collection; ...@@ -30,6 +30,7 @@ import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.jar.JarInputStream; import java.util.jar.JarInputStream;
...@@ -88,16 +89,36 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -88,16 +89,36 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
* Write all entries from the specified jar file. * Write all entries from the specified jar file.
* @param jarFile the source jar file * @param jarFile the source jar file
* @throws IOException if the entries cannot be written * @throws IOException if the entries cannot be written
* @deprecated since 2.4.8 for removal in 2.6.0 in favor of
* {@link #writeEntries(JarFile, EntryTransformer, UnpackHandler, Predicate)}
*/ */
@Deprecated
public void writeEntries(JarFile jarFile) throws IOException { public void writeEntries(JarFile jarFile) throws IOException {
writeEntries(jarFile, EntryTransformer.NONE, UnpackHandler.NEVER); writeEntries(jarFile, EntryTransformer.NONE, UnpackHandler.NEVER, (entry) -> true);
} }
final void writeEntries(JarFile jarFile, EntryTransformer entryTransformer, UnpackHandler unpackHandler) /**
throws IOException { * Write required entries from the specified jar file.
* @param jarFile the source jar file
* @param entryTransformer the entity transformer used to change the entry
* @param unpackHandler the unpack handler
* @param entryFilter a predicate used to filter the written entries
* @throws IOException if the entries cannot be written
* @since 2.4.8
*/
public void writeEntries(JarFile jarFile, EntryTransformer entryTransformer, UnpackHandler unpackHandler,
Predicate<JarEntry> entryFilter) throws IOException {
Enumeration<JarEntry> entries = jarFile.entries(); Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
JarArchiveEntry entry = new JarArchiveEntry(entries.nextElement()); JarEntry entry = entries.nextElement();
if (entryFilter.test(entry)) {
writeEntry(jarFile, entryTransformer, unpackHandler, new JarArchiveEntry(entry));
}
}
}
private void writeEntry(JarFile jarFile, EntryTransformer entryTransformer, UnpackHandler unpackHandler,
JarArchiveEntry entry) throws IOException {
setUpEntry(jarFile, entry); setUpEntry(jarFile, entry);
try (ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream(jarFile.getInputStream(entry))) { try (ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream(jarFile.getInputStream(entry))) {
EntryWriter entryWriter = new InputStreamEntryWriter(inputStream); EntryWriter entryWriter = new InputStreamEntryWriter(inputStream);
...@@ -107,7 +128,6 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -107,7 +128,6 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
} }
} }
} }
}
private void setUpEntry(JarFile jarFile, JarArchiveEntry entry) throws IOException { private void setUpEntry(JarFile jarFile, JarArchiveEntry entry) throws IOException {
try (ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream(jarFile.getInputStream(entry))) { try (ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream(jarFile.getInputStream(entry))) {
......
...@@ -42,7 +42,7 @@ public class JarModeLibrary extends Library { ...@@ -42,7 +42,7 @@ public class JarModeLibrary extends Library {
} }
public JarModeLibrary(LibraryCoordinates coordinates) { public JarModeLibrary(LibraryCoordinates coordinates) {
super(getJarName(coordinates), null, LibraryScope.RUNTIME, coordinates, false); super(getJarName(coordinates), null, LibraryScope.RUNTIME, coordinates, false, false, true);
} }
private static LibraryCoordinates createCoordinates(String artifactId) { private static LibraryCoordinates createCoordinates(String artifactId) {
......
...@@ -43,11 +43,16 @@ public class Library { ...@@ -43,11 +43,16 @@ public class Library {
private final boolean local; private final boolean local;
private final boolean included;
/** /**
* Create a new {@link Library}. * Create a new {@link Library}.
* @param file the source file * @param file the source file
* @param scope the scope of the library * @param scope the scope of the library
* @deprecated since 2.4.8 for removal in 2.6.0 in favor of
* {@link #Library(String, File, LibraryScope, LibraryCoordinates, boolean, boolean, boolean)}
*/ */
@Deprecated
public Library(File file, LibraryScope scope) { public Library(File file, LibraryScope scope) {
this(file, scope, false); this(file, scope, false);
} }
...@@ -57,7 +62,10 @@ public class Library { ...@@ -57,7 +62,10 @@ public class Library {
* @param file the source file * @param file the source file
* @param scope the scope of the library * @param scope the scope of the library
* @param unpackRequired if the library needs to be unpacked before it can be used * @param unpackRequired if the library needs to be unpacked before it can be used
* @deprecated since 2.4.8 for removal in 2.6.0 in favor of
* {@link #Library(String, File, LibraryScope, LibraryCoordinates, boolean, boolean, boolean)}
*/ */
@Deprecated
public Library(File file, LibraryScope scope, boolean unpackRequired) { public Library(File file, LibraryScope scope, boolean unpackRequired) {
this(null, file, scope, unpackRequired); this(null, file, scope, unpackRequired);
} }
...@@ -69,7 +77,10 @@ public class Library { ...@@ -69,7 +77,10 @@ public class Library {
* @param file the source file * @param file the source file
* @param scope the scope of the library * @param scope the scope of the library
* @param unpackRequired if the library needs to be unpacked before it can be used * @param unpackRequired if the library needs to be unpacked before it can be used
* @deprecated since 2.4.8 for removal in 2.6.0 in favor of
* {@link #Library(String, File, LibraryScope, LibraryCoordinates, boolean, boolean, boolean)}
*/ */
@Deprecated
public Library(String name, File file, LibraryScope scope, boolean unpackRequired) { public Library(String name, File file, LibraryScope scope, boolean unpackRequired) {
this(name, file, scope, null, unpackRequired); this(name, file, scope, null, unpackRequired);
} }
...@@ -82,7 +93,10 @@ public class Library { ...@@ -82,7 +93,10 @@ public class Library {
* @param scope the scope of the library * @param scope the scope of the library
* @param coordinates the library coordinates or {@code null} * @param coordinates the library coordinates or {@code null}
* @param unpackRequired if the library needs to be unpacked before it can be used * @param unpackRequired if the library needs to be unpacked before it can be used
* @deprecated since 2.4.8 for removal in 2.6.0 in favor of
* {@link #Library(String, File, LibraryScope, LibraryCoordinates, boolean, boolean, boolean)}
*/ */
@Deprecated
public Library(String name, File file, LibraryScope scope, LibraryCoordinates coordinates, boolean unpackRequired) { public Library(String name, File file, LibraryScope scope, LibraryCoordinates coordinates, boolean unpackRequired) {
this(name, file, scope, coordinates, unpackRequired, false); this(name, file, scope, coordinates, unpackRequired, false);
} }
...@@ -98,15 +112,37 @@ public class Library { ...@@ -98,15 +112,37 @@ public class Library {
* @param local if the library is local (part of the same build) to the application * @param local if the library is local (part of the same build) to the application
* that is being packaged * that is being packaged
* @since 2.4.0 * @since 2.4.0
* @deprecated since 2.4.8 for removal in 2.6.0 in favor of
* {@link #Library(String, File, LibraryScope, LibraryCoordinates, boolean, boolean, boolean)}
*/ */
@Deprecated
public Library(String name, File file, LibraryScope scope, LibraryCoordinates coordinates, boolean unpackRequired, public Library(String name, File file, LibraryScope scope, LibraryCoordinates coordinates, boolean unpackRequired,
boolean local) { boolean local) {
this(name, file, scope, coordinates, unpackRequired, local, true);
}
/**
* Create a new {@link Library}.
* @param name the name of the library as it should be written or {@code null} to use
* the file name
* @param file the source file
* @param scope the scope of the library
* @param coordinates the library coordinates or {@code null}
* @param unpackRequired if the library needs to be unpacked before it can be used
* @param local if the library is local (part of the same build) to the application
* that is being packaged
* @param included if the library is included in the fat jar
* @since 2.4.8
*/
public Library(String name, File file, LibraryScope scope, LibraryCoordinates coordinates, boolean unpackRequired,
boolean local, boolean included) {
this.name = (name != null) ? name : file.getName(); this.name = (name != null) ? name : file.getName();
this.file = file; this.file = file;
this.scope = scope; this.scope = scope;
this.coordinates = coordinates; this.coordinates = coordinates;
this.unpackRequired = unpackRequired; this.unpackRequired = unpackRequired;
this.local = local; this.local = local;
this.included = included;
} }
/** /**
...@@ -172,4 +208,12 @@ public class Library { ...@@ -172,4 +208,12 @@ public class Library {
return this.local; return this.local;
} }
/**
* Return if the library is included in the fat jar.
* @return if the library is included
*/
public boolean isIncluded() {
return this.included;
}
} }
...@@ -25,7 +25,9 @@ import java.util.List; ...@@ -25,7 +25,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -191,14 +193,18 @@ public abstract class Packager { ...@@ -191,14 +193,18 @@ public abstract class Packager {
protected final void write(JarFile sourceJar, Libraries libraries, AbstractJarWriter writer) throws IOException { protected final void write(JarFile sourceJar, Libraries libraries, AbstractJarWriter writer) throws IOException {
Assert.notNull(libraries, "Libraries must not be null"); Assert.notNull(libraries, "Libraries must not be null");
WritableLibraries writeableLibraries = new WritableLibraries(libraries); write(sourceJar, writer, new PackagedLibraries(libraries));
}
private void write(JarFile sourceJar, AbstractJarWriter writer, PackagedLibraries libraries) throws IOException {
if (isLayered()) { if (isLayered()) {
writer.useLayers(this.layers, this.layersIndex); writer.useLayers(this.layers, this.layersIndex);
} }
writer.writeManifest(buildManifest(sourceJar)); writer.writeManifest(buildManifest(sourceJar));
writeLoaderClasses(writer); writeLoaderClasses(writer);
writer.writeEntries(sourceJar, getEntityTransformer(), writeableLibraries); writer.writeEntries(sourceJar, getEntityTransformer(), libraries.getUnpackHandler(),
writeableLibraries.write(writer); libraries.getEntryFilter());
libraries.write(writer);
if (isLayered()) { if (isLayered()) {
writeLayerIndex(writer); writeLayerIndex(writer);
} }
...@@ -456,11 +462,15 @@ public abstract class Packager { ...@@ -456,11 +462,15 @@ public abstract class Packager {
* An {@link UnpackHandler} that determines that an entry needs to be unpacked if a * An {@link UnpackHandler} that determines that an entry needs to be unpacked if a
* library that requires unpacking has a matching entry name. * library that requires unpacking has a matching entry name.
*/ */
private final class WritableLibraries implements UnpackHandler { private final class PackagedLibraries {
private final Map<String, Library> libraries = new LinkedHashMap<>(); private final Map<String, Library> libraries = new LinkedHashMap<>();
WritableLibraries(Libraries libraries) throws IOException { private final UnpackHandler unpackHandler;
private final Predicate<JarEntry> entryFilter;
PackagedLibraries(Libraries libraries) throws IOException {
libraries.doWithLibraries((library) -> { libraries.doWithLibraries((library) -> {
if (isZip(library::openStream)) { if (isZip(library::openStream)) {
addLibrary(library); addLibrary(library);
...@@ -469,6 +479,8 @@ public abstract class Packager { ...@@ -469,6 +479,8 @@ public abstract class Packager {
if (isLayered() && Packager.this.includeRelevantJarModeJars) { if (isLayered() && Packager.this.includeRelevantJarModeJars) {
addLibrary(JarModeLibrary.LAYER_TOOLS); addLibrary(JarModeLibrary.LAYER_TOOLS);
} }
this.unpackHandler = new PackagedLibrariesUnpackHandler();
this.entryFilter = this::isIncluded;
} }
private void addLibrary(Library library) { private void addLibrary(Library library) {
...@@ -480,37 +492,58 @@ public abstract class Packager { ...@@ -480,37 +492,58 @@ public abstract class Packager {
} }
} }
@Override private boolean isIncluded(JarEntry entry) {
public boolean requiresUnpack(String name) { Library library = this.libraries.get(entry.getName());
Library library = this.libraries.get(name); return library == null || library.isIncluded();
return library != null && library.isUnpackRequired();
} }
@Override UnpackHandler getUnpackHandler() {
public String sha1Hash(String name) throws IOException { return this.unpackHandler;
Library library = this.libraries.get(name);
Assert.notNull(library, () -> "No library found for entry name '" + name + "'");
return Digest.sha1(library::openStream);
} }
private void write(AbstractJarWriter writer) throws IOException { Predicate<JarEntry> getEntryFilter() {
return this.entryFilter;
}
void write(AbstractJarWriter writer) throws IOException {
List<String> writtenPaths = new ArrayList<>();
for (Entry<String, Library> entry : this.libraries.entrySet()) { for (Entry<String, Library> entry : this.libraries.entrySet()) {
String path = entry.getKey(); String path = entry.getKey();
Library library = entry.getValue(); Library library = entry.getValue();
if (library.isIncluded()) {
String location = path.substring(0, path.lastIndexOf('/') + 1); String location = path.substring(0, path.lastIndexOf('/') + 1);
writer.writeNestedLibrary(location, library); writer.writeNestedLibrary(location, library);
writtenPaths.add(path);
}
} }
if (getLayout() instanceof RepackagingLayout) { if (getLayout() instanceof RepackagingLayout) {
writeClasspathIndex((RepackagingLayout) getLayout(), writer); writeClasspathIndex(writtenPaths, (RepackagingLayout) getLayout(), writer);
} }
} }
private void writeClasspathIndex(RepackagingLayout layout, AbstractJarWriter writer) throws IOException { private void writeClasspathIndex(List<String> paths, RepackagingLayout layout, AbstractJarWriter writer)
List<String> names = this.libraries.keySet().stream().map((path) -> "- \"" + path + "\"") throws IOException {
.collect(Collectors.toList()); List<String> names = paths.stream().map((path) -> "- \"" + path + "\"").collect(Collectors.toList());
writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names); writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names);
} }
private class PackagedLibrariesUnpackHandler implements UnpackHandler {
@Override
public boolean requiresUnpack(String name) {
Library library = PackagedLibraries.this.libraries.get(name);
return library != null && library.isUnpackRequired();
}
@Override
public String sha1Hash(String name) throws IOException {
Library library = PackagedLibraries.this.libraries.get(name);
Assert.notNull(library, () -> "No library found for entry name '" + name + "'");
return Digest.sha1(library::openStream);
}
}
} }
} }
...@@ -89,4 +89,13 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests { ...@@ -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");
});
}
} }
<?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>
/*
* 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) {
}
}
...@@ -76,7 +76,7 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo { ...@@ -76,7 +76,7 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo {
this.excludeGroupIds = excludeGroupIds; this.excludeGroupIds = excludeGroupIds;
} }
protected Set<Artifact> filterDependencies(Set<Artifact> dependencies, FilterArtifacts filters) protected final Set<Artifact> filterDependencies(Set<Artifact> dependencies, FilterArtifacts filters)
throws MojoExecutionException { throws MojoExecutionException {
try { try {
Set<Artifact> filtered = new LinkedHashSet<>(dependencies); Set<Artifact> filtered = new LinkedHashSet<>(dependencies);
......
...@@ -182,8 +182,9 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo ...@@ -182,8 +182,9 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
* @throws MojoExecutionException on execution error * @throws MojoExecutionException on execution error
*/ */
protected final Libraries getLibraries(Collection<Dependency> unpacks) throws MojoExecutionException { protected final Libraries getLibraries(Collection<Dependency> unpacks) throws MojoExecutionException {
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), getFilters(getAdditionalFilters())); Set<Artifact> artifacts = this.project.getArtifacts();
return new ArtifactsLibraries(artifacts, this.session.getProjects(), unpacks, getLog()); Set<Artifact> includedArtifacts = filterDependencies(artifacts, getFilters(getAdditionalFilters()));
return new ArtifactsLibraries(artifacts, includedArtifacts, this.session.getProjects(), unpacks, getLog());
} }
private ArtifactsFilter[] getAdditionalFilters() { private ArtifactsFilter[] getAdditionalFilters() {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.maven; package org.springframework.boot.maven;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
...@@ -59,6 +60,8 @@ public class ArtifactsLibraries implements Libraries { ...@@ -59,6 +60,8 @@ public class ArtifactsLibraries implements Libraries {
private final Set<Artifact> artifacts; private final Set<Artifact> artifacts;
private final Set<Artifact> includedArtifacts;
private final Collection<MavenProject> localProjects; private final Collection<MavenProject> localProjects;
private final Collection<Dependency> unpacks; private final Collection<Dependency> unpacks;
...@@ -89,7 +92,23 @@ public class ArtifactsLibraries implements Libraries { ...@@ -89,7 +92,23 @@ public class ArtifactsLibraries implements Libraries {
*/ */
public ArtifactsLibraries(Set<Artifact> artifacts, Collection<MavenProject> localProjects, public ArtifactsLibraries(Set<Artifact> artifacts, Collection<MavenProject> localProjects,
Collection<Dependency> unpacks, Log log) { 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.artifacts = artifacts;
this.includedArtifacts = includedArtifacts;
this.localProjects = localProjects; this.localProjects = localProjects;
this.unpacks = unpacks; this.unpacks = unpacks;
this.log = log; this.log = log;
...@@ -99,18 +118,22 @@ public class ArtifactsLibraries implements Libraries { ...@@ -99,18 +118,22 @@ public class ArtifactsLibraries implements Libraries {
public void doWithLibraries(LibraryCallback callback) throws IOException { public void doWithLibraries(LibraryCallback callback) throws IOException {
Set<String> duplicates = getDuplicates(this.artifacts); Set<String> duplicates = getDuplicates(this.artifacts);
for (Artifact artifact : this.artifacts) { for (Artifact artifact : this.artifacts) {
LibraryScope scope = SCOPES.get(artifact.getScope());
if (scope != null && artifact.getFile() != null) {
String name = getFileName(artifact); String name = getFileName(artifact);
File file = artifact.getFile();
LibraryScope scope = SCOPES.get(artifact.getScope());
if (scope == null || file == null) {
continue;
}
if (duplicates.contains(name)) { if (duplicates.contains(name)) {
this.log.debug("Duplicate found: " + name); this.log.debug("Duplicate found: " + name);
name = artifact.getGroupId() + "-" + name; name = artifact.getGroupId() + "-" + name;
this.log.debug("Renamed to: " + name); this.log.debug("Renamed to: " + name);
} }
LibraryCoordinates coordinates = new ArtifactLibraryCoordinates(artifact); LibraryCoordinates coordinates = new ArtifactLibraryCoordinates(artifact);
callback.library(new Library(name, artifact.getFile(), scope, coordinates, isUnpackRequired(artifact), boolean unpackRequired = isUnpackRequired(artifact);
isLocal(artifact))); boolean local = isLocal(artifact);
} boolean included = this.includedArtifacts.contains(artifact);
callback.library(new Library(name, file, scope, coordinates, unpackRequired, local, included));
} }
} }
......
...@@ -189,8 +189,8 @@ public class BuildImageMojo extends AbstractPackagerMojo { ...@@ -189,8 +189,8 @@ public class BuildImageMojo extends AbstractPackagerMojo {
} }
/** /**
* Return the layout factory that will be used to determine the {@link LayoutType} if * Return the layout factory that will be used to determine the
* no explicit layout is set. * {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
* @return the value of the {@code layoutFactory} parameter, or {@code null} if the * @return the value of the {@code layoutFactory} parameter, or {@code null} if the
* parameter is not provided * parameter is not provided
*/ */
......
...@@ -183,8 +183,8 @@ public class RepackageMojo extends AbstractPackagerMojo { ...@@ -183,8 +183,8 @@ public class RepackageMojo extends AbstractPackagerMojo {
} }
/** /**
* Return the layout factory that will be used to determine the {@link LayoutType} if * Return the layout factory that will be used to determine the
* no explicit layout is set. * {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
* @return the value of the {@code layoutFactory} parameter, or {@code null} if the * @return the value of the {@code layoutFactory} parameter, or {@code null} if the
* parameter is not provided * parameter is not provided
*/ */
......
...@@ -144,6 +144,7 @@ class ArtifactsLibrariesTests { ...@@ -144,6 +144,7 @@ class ArtifactsLibrariesTests {
this.artifacts = Collections.singleton(snapshotArtifact); this.artifacts = Collections.singleton(snapshotArtifact);
new ArtifactsLibraries(this.artifacts, Collections.emptyList(), null, mock(Log.class)) new ArtifactsLibraries(this.artifacts, Collections.emptyList(), null, mock(Log.class))
.doWithLibraries((library) -> { .doWithLibraries((library) -> {
assertThat(library.isIncluded()).isTrue();
assertThat(library.isLocal()).isFalse(); assertThat(library.isLocal()).isFalse();
assertThat(library.getCoordinates().getVersion()).isEqualTo("1.0-SNAPSHOT"); assertThat(library.getCoordinates().getVersion()).isEqualTo("1.0-SNAPSHOT");
}); });
...@@ -181,4 +182,19 @@ class ArtifactsLibrariesTests { ...@@ -181,4 +182,19 @@ class ArtifactsLibrariesTests {
.doWithLibraries((library) -> assertThat(library.isLocal()).isTrue()); .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());
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment