commit
af37f59136
|
@ -198,7 +198,7 @@ abstract class ArchiveCommand extends OptionParsingCommand {
|
|||
List<Library> libraries = new ArrayList<>();
|
||||
for (URL dependency : dependencies) {
|
||||
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;
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ abstract class ArchiveCommand extends OptionParsingCommand {
|
|||
List<Library> libraries = new ArrayList<>();
|
||||
for (MatchedResource entry : entries) {
|
||||
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 {
|
||||
writeClasspathEntry(writer, entry);
|
||||
|
|
|
@ -37,7 +37,7 @@ public class MyBuildTool {
|
|||
private void getLibraries(LibraryCallback callback) throws IOException {
|
||||
// Build system specific implementation, callback for each dependency
|
||||
for (File nestedJar : getCompileScopeJars()) {
|
||||
callback.library(new Library(nestedJar, LibraryScope.COMPILE));
|
||||
callback.library(new Library(null, nestedJar, LibraryScope.COMPILE, null, false, false, true));
|
||||
}
|
||||
// ...
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.gradle.api.specs.Spec;
|
|||
import org.springframework.boot.gradle.tasks.bundling.ResolvedDependencies.DependencyDescriptor;
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
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}
|
||||
|
@ -77,9 +78,12 @@ class LayerResolver {
|
|||
private Library asLibrary(FileCopyDetails details) {
|
||||
File file = details.getFile();
|
||||
DependencyDescriptor dependency = this.resolvedDependencies.find(file);
|
||||
return (dependency != null)
|
||||
? new Library(null, file, null, dependency.getCoordinates(), false, dependency.isProjectDependency())
|
||||
: new Library(file, null);
|
||||
if (dependency == null) {
|
||||
return new Library(null, file, null, null, false, false, true);
|
||||
}
|
||||
LibraryCoordinates coordinates = dependency.getCoordinates();
|
||||
boolean projectDependency = dependency.isProjectDependency();
|
||||
return new Library(null, file, null, coordinates, false, projectDependency, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.util.Collection;
|
|||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
@ -89,24 +89,33 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
|
|||
* Write all entries from the specified jar file.
|
||||
* @param jarFile the source jar file
|
||||
* @throws IOException if the entries cannot be written
|
||||
* @deprecated since 2.4.8 for removal in 2.6.0
|
||||
*/
|
||||
@Deprecated
|
||||
public void writeEntries(JarFile jarFile) throws IOException {
|
||||
writeEntries(jarFile, EntryTransformer.NONE, UnpackHandler.NEVER, (name) -> false);
|
||||
writeEntries(jarFile, EntryTransformer.NONE, UnpackHandler.NEVER, (entry) -> null);
|
||||
}
|
||||
|
||||
final void writeEntries(JarFile jarFile, EntryTransformer entryTransformer, UnpackHandler unpackHandler,
|
||||
Predicate<String> libraryPredicate) throws IOException {
|
||||
Function<JarEntry, Library> libraryLookup) throws IOException {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarArchiveEntry entry = new JarArchiveEntry(entries.nextElement());
|
||||
JarEntry entry = entries.nextElement();
|
||||
Library library = libraryLookup.apply(entry);
|
||||
if (library == null || library.isIncluded()) {
|
||||
writeEntry(jarFile, entryTransformer, unpackHandler, new JarArchiveEntry(entry), library);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEntry(JarFile jarFile, EntryTransformer entryTransformer, UnpackHandler unpackHandler,
|
||||
JarArchiveEntry entry, Library library) throws IOException {
|
||||
setUpEntry(jarFile, entry);
|
||||
try (ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream(jarFile.getInputStream(entry))) {
|
||||
EntryWriter entryWriter = new InputStreamEntryWriter(inputStream);
|
||||
JarArchiveEntry transformedEntry = entryTransformer.transform(entry);
|
||||
if (transformedEntry != null) {
|
||||
boolean updateLayerIndex = !libraryPredicate.test(entry.getName());
|
||||
writeEntry(transformedEntry, entryWriter, unpackHandler, updateLayerIndex);
|
||||
}
|
||||
writeEntry(transformedEntry, library, entryWriter, unpackHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,15 +169,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
|
|||
entry.setTime(getNestedLibraryTime(library));
|
||||
new CrcAndSize(library::openStream).setupStoredEntry(entry);
|
||||
try (InputStream inputStream = library.openStream()) {
|
||||
writeEntry(entry, new InputStreamEntryWriter(inputStream), new LibraryUnpackHandler(library), false);
|
||||
updateLayerIndex(entry.getName(), library);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLayerIndex(String name, Library library) {
|
||||
if (this.layers != null) {
|
||||
Layer layer = this.layers.getLayer(library);
|
||||
this.layersIndex.add(layer, name);
|
||||
writeEntry(entry, library, new InputStreamEntryWriter(inputStream), new LibraryUnpackHandler(library));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,20 +250,20 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
|
|||
}
|
||||
|
||||
private void writeEntry(JarArchiveEntry entry, EntryWriter entryWriter) throws IOException {
|
||||
writeEntry(entry, entryWriter, UnpackHandler.NEVER, true);
|
||||
writeEntry(entry, null, entryWriter, UnpackHandler.NEVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the actual write of a {@link JarEntry}. All other write methods delegate to
|
||||
* this one.
|
||||
* @param entry the entry to write
|
||||
* @param library the library for the entry or {@code null}
|
||||
* @param entryWriter the entry writer or {@code null} if there is no content
|
||||
* @param unpackHandler handles possible unpacking for the entry
|
||||
* @param updateLayerIndex if the layer index should be updated
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
private void writeEntry(JarArchiveEntry entry, EntryWriter entryWriter, UnpackHandler unpackHandler,
|
||||
boolean updateLayerIndex) throws IOException {
|
||||
private void writeEntry(JarArchiveEntry entry, Library library, EntryWriter entryWriter,
|
||||
UnpackHandler unpackHandler) throws IOException {
|
||||
String name = entry.getName();
|
||||
writeParentDirectoryEntries(name);
|
||||
if (this.writtenEntries.add(name)) {
|
||||
|
@ -273,16 +274,14 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
|
|||
entry.setSize(entryWriter.size());
|
||||
}
|
||||
entryWriter = addUnpackCommentIfNecessary(entry, entryWriter, unpackHandler);
|
||||
if (updateLayerIndex) {
|
||||
updateLayerIndex(entry);
|
||||
}
|
||||
updateLayerIndex(entry, library);
|
||||
writeToArchive(entry, entryWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLayerIndex(JarArchiveEntry entry) {
|
||||
private void updateLayerIndex(JarArchiveEntry entry, Library library) {
|
||||
if (this.layers != null && !entry.getName().endsWith("/")) {
|
||||
Layer layer = this.layers.getLayer(entry.getName());
|
||||
Layer layer = (library != null) ? this.layers.getLayer(library) : this.layers.getLayer(entry.getName());
|
||||
this.layersIndex.add(layer, entry.getName());
|
||||
}
|
||||
}
|
||||
|
@ -294,7 +293,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
|
|||
while (parent.lastIndexOf('/') != -1) {
|
||||
parent = parent.substring(0, parent.lastIndexOf('/'));
|
||||
if (!parent.isEmpty()) {
|
||||
writeEntry(new JarArchiveEntry(parent + "/"), null, UnpackHandler.NEVER, false);
|
||||
writeEntry(new JarArchiveEntry(parent + "/"), null, null, UnpackHandler.NEVER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public class JarModeLibrary extends Library {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -43,11 +43,16 @@ public class Library {
|
|||
|
||||
private final boolean local;
|
||||
|
||||
private final boolean included;
|
||||
|
||||
/**
|
||||
* Create a new {@link Library}.
|
||||
* @param file the source file
|
||||
* @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) {
|
||||
this(file, scope, false);
|
||||
}
|
||||
|
@ -57,7 +62,10 @@ public class Library {
|
|||
* @param file the source file
|
||||
* @param scope the scope of the library
|
||||
* @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) {
|
||||
this(null, file, scope, unpackRequired);
|
||||
}
|
||||
|
@ -69,7 +77,10 @@ public class Library {
|
|||
* @param file the source file
|
||||
* @param scope the scope of the library
|
||||
* @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) {
|
||||
this(name, file, scope, null, unpackRequired);
|
||||
}
|
||||
|
@ -82,7 +93,10 @@ public class Library {
|
|||
* @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
|
||||
* @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) {
|
||||
this(name, file, scope, coordinates, unpackRequired, false);
|
||||
}
|
||||
|
@ -98,15 +112,37 @@ public class Library {
|
|||
* @param local if the library is local (part of the same build) to the application
|
||||
* that is being packaged
|
||||
* @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,
|
||||
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.file = file;
|
||||
this.scope = scope;
|
||||
this.coordinates = coordinates;
|
||||
this.unpackRequired = unpackRequired;
|
||||
this.local = local;
|
||||
this.included = included;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,4 +208,12 @@ public class Library {
|
|||
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;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -191,14 +193,18 @@ public abstract class Packager {
|
|||
|
||||
protected final void write(JarFile sourceJar, Libraries libraries, AbstractJarWriter writer) throws IOException {
|
||||
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()) {
|
||||
writer.useLayers(this.layers, this.layersIndex);
|
||||
}
|
||||
writer.writeManifest(buildManifest(sourceJar));
|
||||
writeLoaderClasses(writer);
|
||||
writer.writeEntries(sourceJar, getEntityTransformer(), writeableLibraries, writeableLibraries::containsEntry);
|
||||
writeableLibraries.write(writer);
|
||||
writer.writeEntries(sourceJar, getEntityTransformer(), libraries.getUnpackHandler(),
|
||||
libraries.getLibraryLookup());
|
||||
libraries.write(writer);
|
||||
if (isLayered()) {
|
||||
writeLayerIndex(writer);
|
||||
}
|
||||
|
@ -451,11 +457,15 @@ public abstract class Packager {
|
|||
* An {@link UnpackHandler} that determines that an entry needs to be unpacked if a
|
||||
* 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<>();
|
||||
|
||||
WritableLibraries(Libraries libraries) throws IOException {
|
||||
private final UnpackHandler unpackHandler;
|
||||
|
||||
private final Function<JarEntry, Library> libraryLookup;
|
||||
|
||||
PackagedLibraries(Libraries libraries) throws IOException {
|
||||
libraries.doWithLibraries((library) -> {
|
||||
if (isZip(library::openStream)) {
|
||||
addLibrary(library);
|
||||
|
@ -464,6 +474,8 @@ public abstract class Packager {
|
|||
if (isLayered() && Packager.this.includeRelevantJarModeJars) {
|
||||
addLibrary(JarModeLibrary.LAYER_TOOLS);
|
||||
}
|
||||
this.unpackHandler = new PackagedLibrariesUnpackHandler();
|
||||
this.libraryLookup = this::lookup;
|
||||
}
|
||||
|
||||
private void addLibrary(Library library) {
|
||||
|
@ -475,39 +487,55 @@ public abstract class Packager {
|
|||
}
|
||||
}
|
||||
|
||||
private Library lookup(JarEntry entry) {
|
||||
return this.libraries.get(entry.getName());
|
||||
}
|
||||
|
||||
UnpackHandler getUnpackHandler() {
|
||||
return this.unpackHandler;
|
||||
}
|
||||
|
||||
Function<JarEntry, Library> getLibraryLookup() {
|
||||
return this.libraryLookup;
|
||||
}
|
||||
|
||||
void write(AbstractJarWriter writer) throws IOException {
|
||||
List<String> writtenPaths = new ArrayList<>();
|
||||
for (Entry<String, Library> entry : this.libraries.entrySet()) {
|
||||
String path = entry.getKey();
|
||||
Library library = entry.getValue();
|
||||
if (library.isIncluded()) {
|
||||
String location = path.substring(0, path.lastIndexOf('/') + 1);
|
||||
writer.writeNestedLibrary(location, library);
|
||||
writtenPaths.add(path);
|
||||
}
|
||||
}
|
||||
if (getLayout() instanceof RepackagingLayout) {
|
||||
writeClasspathIndex(writtenPaths, (RepackagingLayout) getLayout(), writer);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeClasspathIndex(List<String> paths, RepackagingLayout layout, AbstractJarWriter writer)
|
||||
throws IOException {
|
||||
List<String> names = paths.stream().map((path) -> "- \"" + path + "\"").collect(Collectors.toList());
|
||||
writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names);
|
||||
}
|
||||
|
||||
private class PackagedLibrariesUnpackHandler implements UnpackHandler {
|
||||
|
||||
@Override
|
||||
public boolean requiresUnpack(String name) {
|
||||
Library library = this.libraries.get(name);
|
||||
Library library = PackagedLibraries.this.libraries.get(name);
|
||||
return library != null && library.isUnpackRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1Hash(String name) throws IOException {
|
||||
Library library = this.libraries.get(name);
|
||||
Library library = PackagedLibraries.this.libraries.get(name);
|
||||
Assert.notNull(library, () -> "No library found for entry name '" + name + "'");
|
||||
return Digest.sha1(library::openStream);
|
||||
}
|
||||
|
||||
boolean containsEntry(String name) {
|
||||
return this.libraries.containsKey(name);
|
||||
}
|
||||
|
||||
private void write(AbstractJarWriter writer) throws IOException {
|
||||
for (Entry<String, Library> entry : this.libraries.entrySet()) {
|
||||
String path = entry.getKey();
|
||||
Library library = entry.getValue();
|
||||
String location = path.substring(0, path.lastIndexOf('/') + 1);
|
||||
writer.writeNestedLibrary(location, library);
|
||||
}
|
||||
if (Packager.this.layout instanceof RepackagingLayout) {
|
||||
writeClasspathIndex(getLayout(), writer);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeClasspathIndex(Layout layout, AbstractJarWriter writer) throws IOException {
|
||||
List<String> names = this.libraries.keySet().stream().map((path) -> "- \"" + path + "\"")
|
||||
.collect(Collectors.toList());
|
||||
writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -197,9 +197,9 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
libJarFile.setLastModified(JAN_1_1980);
|
||||
P packager = createPackager();
|
||||
execute(packager, (callback) -> {
|
||||
callback.library(new Library(libJarFile, LibraryScope.COMPILE));
|
||||
callback.library(new Library(libJarFileToUnpack, LibraryScope.COMPILE, true));
|
||||
callback.library(new Library(libNonJarFile, LibraryScope.COMPILE));
|
||||
callback.library(newLibrary(libJarFile, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libJarFileToUnpack, LibraryScope.COMPILE, true));
|
||||
callback.library(newLibrary(libNonJarFile, LibraryScope.COMPILE, false));
|
||||
});
|
||||
assertThat(hasPackagedEntry("BOOT-INF/lib/" + libJarFile.getName())).isTrue();
|
||||
assertThat(hasPackagedEntry("BOOT-INF/lib/" + libJarFileToUnpack.getName())).isTrue();
|
||||
|
@ -226,9 +226,9 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
File file = this.testJarFile.getFile();
|
||||
P packager = createPackager(file);
|
||||
execute(packager, (callback) -> {
|
||||
callback.library(new Library(libJarFile1, LibraryScope.COMPILE));
|
||||
callback.library(new Library(libJarFile2, LibraryScope.COMPILE));
|
||||
callback.library(new Library(libJarFile3, LibraryScope.COMPILE));
|
||||
callback.library(newLibrary(libJarFile1, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libJarFile2, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libJarFile3, LibraryScope.COMPILE, false));
|
||||
});
|
||||
assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue();
|
||||
String index = getPackagedEntryContent("BOOT-INF/classpath.idx");
|
||||
|
@ -258,9 +258,9 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
packager.setLayers(layers);
|
||||
packager.setIncludeRelevantJarModeJars(false);
|
||||
execute(packager, (callback) -> {
|
||||
callback.library(new Library(libJarFile1, LibraryScope.COMPILE));
|
||||
callback.library(new Library(libJarFile2, LibraryScope.COMPILE));
|
||||
callback.library(new Library(libJarFile3, LibraryScope.COMPILE));
|
||||
callback.library(newLibrary(libJarFile1, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libJarFile2, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libJarFile3, LibraryScope.COMPILE, false));
|
||||
});
|
||||
assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue();
|
||||
String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx");
|
||||
|
@ -316,8 +316,8 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class);
|
||||
P packager = createPackager();
|
||||
assertThatIllegalStateException().isThrownBy(() -> execute(packager, (callback) -> {
|
||||
callback.library(new Library(libJarFile, LibraryScope.COMPILE, false));
|
||||
callback.library(new Library(libJarFile, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libJarFile, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libJarFile, LibraryScope.COMPILE, false));
|
||||
})).withMessageContaining("Duplicate library");
|
||||
}
|
||||
|
||||
|
@ -334,7 +334,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
given(layout.getLibraryLocation(anyString(), eq(scope))).willReturn("test/");
|
||||
given(layout.getLibraryLocation(anyString(), eq(LibraryScope.COMPILE))).willReturn("test-lib/");
|
||||
packager.setLayout(layout);
|
||||
execute(packager, (callback) -> callback.library(new Library(libJarFile, scope)));
|
||||
execute(packager, (callback) -> callback.library(newLibrary(libJarFile, scope, false)));
|
||||
assertThat(hasPackagedEntry("test/" + libJarFile.getName())).isTrue();
|
||||
assertThat(getPackagedManifest().getMainAttributes().getValue("Spring-Boot-Lib")).isEqualTo("test-lib/");
|
||||
assertThat(getPackagedManifest().getMainAttributes().getValue("Main-Class")).isEqualTo("testLauncher");
|
||||
|
@ -351,7 +351,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
LibraryScope scope = mock(LibraryScope.class);
|
||||
given(layout.getLauncherClassName()).willReturn("testLauncher");
|
||||
packager.setLayout(layout);
|
||||
execute(packager, (callback) -> callback.library(new Library(libJarFile, scope)));
|
||||
execute(packager, (callback) -> callback.library(newLibrary(libJarFile, scope, false)));
|
||||
assertThat(getPackagedManifest().getMainAttributes().getValue("Spring-Boot-Lib")).isNull();
|
||||
assertThat(getPackagedManifest().getMainAttributes().getValue("Main-Class")).isEqualTo("testLauncher");
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
this.testJarFile.addFile("test/nested.jar", nestedFile);
|
||||
this.testJarFile.addClass("A.class", ClassWithMainMethod.class);
|
||||
P packager = createPackager();
|
||||
execute(packager, (callback) -> callback.library(new Library(nestedFile, LibraryScope.COMPILE)));
|
||||
execute(packager, (callback) -> callback.library(newLibrary(nestedFile, LibraryScope.COMPILE, false)));
|
||||
assertThat(getPackagedEntry("BOOT-INF/lib/" + nestedFile.getName()).getMethod()).isEqualTo(ZipEntry.STORED);
|
||||
assertThat(getPackagedEntry("BOOT-INF/classes/test/nested.jar").getMethod()).isEqualTo(ZipEntry.STORED);
|
||||
}
|
||||
|
@ -419,7 +419,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
this.testJarFile.addFile(name, nested.getFile());
|
||||
this.testJarFile.addClass("A.class", ClassWithMainMethod.class);
|
||||
P packager = createPackager();
|
||||
execute(packager, (callback) -> callback.library(new Library(nestedFile, LibraryScope.COMPILE, true)));
|
||||
execute(packager, (callback) -> callback.library(newLibrary(nestedFile, LibraryScope.COMPILE, true)));
|
||||
assertThat(getPackagedEntry(name).getComment()).startsWith("UNPACK:");
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
File toZip = new File(this.tempDir, "to-zip");
|
||||
toZip.createNewFile();
|
||||
ZipUtil.packEntry(toZip, nestedFile);
|
||||
callback.library(new Library(nestedFile, LibraryScope.COMPILE));
|
||||
callback.library(newLibrary(nestedFile, LibraryScope.COMPILE, false));
|
||||
});
|
||||
assertThat(getPackagedEntry("BOOT-INF/lib/" + nestedFile.getName()).getSize()).isEqualTo(sourceLength);
|
||||
}
|
||||
|
@ -498,14 +498,14 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
@Test
|
||||
void loaderIsWrittenFirstThenApplicationClassesThenLibraries() throws IOException {
|
||||
this.testJarFile.addClass("com/example/Application.class", ClassWithMainMethod.class);
|
||||
File libraryOne = createLibrary();
|
||||
File libraryTwo = createLibrary();
|
||||
File libraryThree = createLibrary();
|
||||
File libraryOne = createLibraryJar();
|
||||
File libraryTwo = createLibraryJar();
|
||||
File libraryThree = createLibraryJar();
|
||||
P packager = createPackager();
|
||||
execute(packager, (callback) -> {
|
||||
callback.library(new Library(libraryOne, LibraryScope.COMPILE, false));
|
||||
callback.library(new Library(libraryTwo, LibraryScope.COMPILE, true));
|
||||
callback.library(new Library(libraryThree, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libraryOne, LibraryScope.COMPILE, false));
|
||||
callback.library(newLibrary(libraryTwo, LibraryScope.COMPILE, true));
|
||||
callback.library(newLibrary(libraryThree, LibraryScope.COMPILE, false));
|
||||
});
|
||||
assertThat(getPackagedEntryNames()).containsSubsequence("org/springframework/boot/loader/",
|
||||
"BOOT-INF/classes/com/example/Application.class", "BOOT-INF/lib/" + libraryOne.getName(),
|
||||
|
@ -514,12 +514,12 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
|
||||
@Test
|
||||
void existingEntryThatMatchesUnpackLibraryIsMarkedForUnpack() throws IOException {
|
||||
File library = createLibrary();
|
||||
File library = createLibraryJar();
|
||||
this.testJarFile.addClass("WEB-INF/classes/com/example/Application.class", ClassWithMainMethod.class);
|
||||
this.testJarFile.addFile("WEB-INF/lib/" + library.getName(), library);
|
||||
P packager = createPackager(this.testJarFile.getFile("war"));
|
||||
packager.setLayout(new Layouts.War());
|
||||
execute(packager, (callback) -> callback.library(new Library(library, LibraryScope.COMPILE, true)));
|
||||
execute(packager, (callback) -> callback.library(newLibrary(library, LibraryScope.COMPILE, true)));
|
||||
assertThat(getPackagedEntryNames()).containsSubsequence("org/springframework/boot/loader/",
|
||||
"WEB-INF/classes/com/example/Application.class", "WEB-INF/lib/" + library.getName());
|
||||
ZipEntry unpackLibrary = getPackagedEntry("WEB-INF/lib/" + library.getName());
|
||||
|
@ -536,7 +536,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
Layout layout = mock(Layout.class);
|
||||
LibraryScope scope = mock(LibraryScope.class);
|
||||
packager.setLayout(layout);
|
||||
execute(packager, (callback) -> callback.library(new Library(libJarFile, scope)));
|
||||
execute(packager, (callback) -> callback.library(newLibrary(libJarFile, scope, false)));
|
||||
assertThat(getPackagedEntryNames()).containsExactly("META-INF/", "META-INF/MANIFEST.MF", "a/", "a/b/",
|
||||
"a/b/C.class");
|
||||
}
|
||||
|
@ -583,12 +583,39 @@ abstract class AbstractPackagerTests<P extends Packager> {
|
|||
assertThat(getPackagedEntry("BOOT-INF/classes/META-INF/test.kotlin_module")).isNotNull();
|
||||
}
|
||||
|
||||
private File createLibrary() throws IOException {
|
||||
@Test
|
||||
void entryFiltering() throws Exception {
|
||||
File webLibrary = createLibraryJar();
|
||||
File libraryOne = createLibraryJar();
|
||||
File libraryTwo = createLibraryJar();
|
||||
this.testJarFile.addClass("WEB-INF/classes/com/example/Application.class", ClassWithMainMethod.class);
|
||||
this.testJarFile.addFile("WEB-INF/lib/" + webLibrary.getName(), webLibrary);
|
||||
P packager = createPackager(this.testJarFile.getFile("war"));
|
||||
packager.setLayout(new Layouts.War());
|
||||
execute(packager, (callback) -> {
|
||||
callback.library(newLibrary(webLibrary, LibraryScope.COMPILE, false, false));
|
||||
callback.library(newLibrary(libraryOne, LibraryScope.COMPILE, false, false));
|
||||
callback.library(newLibrary(libraryTwo, LibraryScope.COMPILE, false, true));
|
||||
});
|
||||
Collection<String> packagedEntryNames = getPackagedEntryNames();
|
||||
packagedEntryNames.removeIf((name) -> !name.endsWith(".jar"));
|
||||
assertThat(packagedEntryNames).containsExactly("WEB-INF/lib/" + libraryTwo.getName());
|
||||
}
|
||||
|
||||
private File createLibraryJar() throws IOException {
|
||||
TestJarFile library = new TestJarFile(this.tempDir);
|
||||
library.addClass("com/example/library/Library.class", ClassWithoutMainMethod.class);
|
||||
return library.getFile();
|
||||
}
|
||||
|
||||
private Library newLibrary(File file, LibraryScope scope, boolean unpackRequired) {
|
||||
return new Library(null, file, scope, null, unpackRequired, false, true);
|
||||
}
|
||||
|
||||
private Library newLibrary(File file, LibraryScope scope, boolean unpackRequired, boolean included) {
|
||||
return new Library(null, file, scope, null, unpackRequired, false, included);
|
||||
}
|
||||
|
||||
protected final P createPackager() throws IOException {
|
||||
return createPackager(this.testJarFile.getFile());
|
||||
}
|
||||
|
|
|
@ -190,4 +190,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");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<html></html>
|
|
@ -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);
|
||||
|
|
|
@ -182,11 +182,9 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
* @throws MojoExecutionException on execution error
|
||||
*/
|
||||
protected final Libraries getLibraries(Collection<Dependency> unpacks) throws MojoExecutionException {
|
||||
String packaging = this.project.getPackaging();
|
||||
Set<Artifact> projectArtifacts = this.project.getArtifacts();
|
||||
Set<Artifact> artifacts = ("war".equals(packaging)) ? projectArtifacts
|
||||
: filterDependencies(projectArtifacts, 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() {
|
||||
|
|
|
@ -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) {
|
||||
LibraryScope scope = SCOPES.get(artifact.getScope());
|
||||
if (scope != null && artifact.getFile() != null) {
|
||||
String name = getFileName(artifact);
|
||||
File file = artifact.getFile();
|
||||
LibraryScope scope = SCOPES.get(artifact.getScope());
|
||||
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);
|
||||
callback.library(new Library(name, artifact.getFile(), scope, coordinates, isUnpackRequired(artifact),
|
||||
isLocal(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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue