Commit 4e3cdf93 authored by Madhura Bhave's avatar Madhura Bhave Committed by Phillip Webb

Support flat jar layering with Maven

Update the Maven plugin so that layered jars now use the regular "flat"
format. The layers.idx file now describes which layer each file should
be placed.

See gh-20813
Co-authored-by: 's avatarPhillip Webb <pwebb@pivotal.io>
parent 3f806aa5
...@@ -86,7 +86,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -86,7 +86,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
JarArchiveEntry entry = new JarArchiveEntry(entries.nextElement()); JarArchiveEntry entry = new JarArchiveEntry(entries.nextElement());
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, false);
JarArchiveEntry transformedEntry = entryTransformer.transform(entry); JarArchiveEntry transformedEntry = entryTransformer.transform(entry);
if (transformedEntry != null) { if (transformedEntry != null) {
writeEntry(transformedEntry, entryWriter, unpackHandler); writeEntry(transformedEntry, entryWriter, unpackHandler);
...@@ -114,13 +114,18 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -114,13 +114,18 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
*/ */
@Override @Override
public void writeEntry(String entryName, InputStream inputStream) throws IOException { public void writeEntry(String entryName, InputStream inputStream) throws IOException {
writeEntry(entryName, new InputStreamEntryWriter(inputStream, true));
}
/**
* Writes an entry. The {@code inputStream} is closed once the entry has been written
* @param entryName the name of the entry
* @param entryWriter the entry writer
* @throws IOException if the write fails
*/
public void writeEntry(String entryName, EntryWriter entryWriter) throws IOException {
JarArchiveEntry entry = new JarArchiveEntry(entryName); JarArchiveEntry entry = new JarArchiveEntry(entryName);
try { writeEntry(entry, entryWriter);
writeEntry(entry, new InputStreamEntryWriter(inputStream));
}
finally {
inputStream.close();
}
} }
/** /**
...@@ -133,9 +138,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -133,9 +138,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
JarArchiveEntry entry = new JarArchiveEntry(location + library.getName()); JarArchiveEntry entry = new JarArchiveEntry(location + library.getName());
entry.setTime(getNestedLibraryTime(library)); entry.setTime(getNestedLibraryTime(library));
new CrcAndSize(library::openStream).setupStoredEntry(entry); new CrcAndSize(library::openStream).setupStoredEntry(entry);
try (InputStream inputStream = library.openStream()) { writeEntry(entry, new InputStreamEntryWriter(library.openStream(), true), new LibraryUnpackHandler(library));
writeEntry(entry, new InputStreamEntryWriter(inputStream), new LibraryUnpackHandler(library));
}
} }
/** /**
...@@ -200,7 +203,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -200,7 +203,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
JarEntry entry; JarEntry entry;
while ((entry = inputStream.getNextJarEntry()) != null) { while ((entry = inputStream.getNextJarEntry()) != null) {
if (entry.getName().endsWith(".class")) { if (entry.getName().endsWith(".class")) {
writeEntry(new JarArchiveEntry(entry), new InputStreamEntryWriter(inputStream)); writeEntry(new JarArchiveEntry(entry), new InputStreamEntryWriter(inputStream, false));
} }
} }
} }
...@@ -254,7 +257,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -254,7 +257,7 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();
entryWriter.write(output); entryWriter.write(output);
entry.setComment("UNPACK:" + unpackHandler.sha1Hash(entry.getName())); entry.setComment("UNPACK:" + unpackHandler.sha1Hash(entry.getName()));
return new InputStreamEntryWriter(new ByteArrayInputStream(output.toByteArray())); return new InputStreamEntryWriter(new ByteArrayInputStream(output.toByteArray()), true);
} }
/** /**
...@@ -264,8 +267,11 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -264,8 +267,11 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
private final InputStream inputStream; private final InputStream inputStream;
InputStreamEntryWriter(InputStream inputStream) { private final boolean close;
InputStreamEntryWriter(InputStream inputStream, boolean close) {
this.inputStream = inputStream; this.inputStream = inputStream;
this.close = close;
} }
@Override @Override
...@@ -276,6 +282,9 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter { ...@@ -276,6 +282,9 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
outputStream.write(buffer, 0, bytesRead); outputStream.write(buffer, 0, bytesRead);
} }
outputStream.flush(); outputStream.flush();
if (this.close) {
this.inputStream.close();
}
} }
} }
......
...@@ -16,40 +16,66 @@ ...@@ -16,40 +16,66 @@
package org.springframework.boot.loader.tools; package org.springframework.boot.loader.tools;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/** /**
* A specialization of {@link RepackagingLayout} that supports layers in the repackaged * Index describing the layer to which each entry in a jar belongs.
* archive.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Andy Wilkinson
* @since 2.3.0 * @since 2.3.0
*/ */
public interface LayeredLayout extends RepackagingLayout { public class LayersIndex {
private final Iterable<Layer> layers;
private final MultiValueMap<Layer, String> index = new LinkedMultiValueMap<>();
/** /**
* Returns the location of the layers index file that should be written or * Create a new {@link LayersIndex} backed by the given layers.
* {@code null} if not index is required. The result should include the filename and * @param layers the layers in the index
* is relative to the root of the jar.
* @return the layers index file location
*/ */
String getLayersIndexFileLocation(); public LayersIndex(Iterable<Layer> layers) {
this.layers = layers;
}
/** /**
* Returns the location to which classes should be moved within the context of a * Add an item to the index.
* layer. * @param layer the layer of the item
* @param layer the destination layer for the content * @param name the name of the item
* @return the repackaged classes location
*/ */
String getRepackagedClassesLocation(Layer layer); public void add(Layer layer, String name) {
this.index.add(layer, name);
}
/** /**
* Returns the destination path for a given library within the context of a layer. * Write the layer index to an output stream.
* @param libraryName the name of the library (excluding any path) * @param out the destination stream
* @param scope the scope of the library * @throws IOException on IO error
* @param layer the destination layer for the content
* @return the location of the library relative to the root of the archive (should end
* with '/') or {@code null} if the library should not be included.
*/ */
String getLibraryLocation(String libraryName, LibraryScope scope, Layer layer); public void writeTo(OutputStream out) throws IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
for (Layer layer : this.layers) {
List<String> names = this.index.get(layer);
if (names != null) {
for (String name : names) {
writer.write(layer.toString());
writer.write(" ");
writer.write(name);
writer.write("\n");
}
}
}
writer.flush();
}
} }
...@@ -94,31 +94,14 @@ public final class Layouts { ...@@ -94,31 +94,14 @@ public final class Layouts {
return "BOOT-INF/classpath.idx"; return "BOOT-INF/classpath.idx";
} }
@Override
public boolean isExecutable() {
return true;
}
}
/**
* Executable JAR layout with support for layers.
*/
public static class LayeredJar extends Jar implements LayeredLayout {
@Override @Override
public String getLayersIndexFileLocation() { public String getLayersIndexFileLocation() {
return "BOOT-INF/layers.idx"; return "BOOT-INF/layers.idx";
} }
@Override @Override
public String getRepackagedClassesLocation(Layer layer) { public boolean isExecutable() {
return "BOOT-INF/layers/" + layer + "/classes/"; return true;
}
@Override
public String getLibraryLocation(String libraryName, LibraryScope scope, Layer layer) {
return "BOOT-INF/layers/" + layer + "/lib/";
} }
} }
......
...@@ -29,6 +29,7 @@ import java.util.jar.Attributes; ...@@ -29,6 +29,7 @@ import java.util.jar.Attributes;
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;
import java.util.zip.ZipEntry;
import org.apache.commons.compress.archivers.jar.JarArchiveEntry; import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
...@@ -79,7 +80,9 @@ public abstract class Packager { ...@@ -79,7 +80,9 @@ public abstract class Packager {
private LayoutFactory layoutFactory; private LayoutFactory layoutFactory;
private Layers layers = Layers.IMPLICIT; private Layers layers;
private LayersIndex layersIndex;
private boolean includeRelevantJarModeJars = true; private boolean includeRelevantJarModeJars = true;
...@@ -136,11 +139,11 @@ public abstract class Packager { ...@@ -136,11 +139,11 @@ public abstract class Packager {
/** /**
* Sets the layers that should be used in the jar. * Sets the layers that should be used in the jar.
* @param layers the jar layers * @param layers the jar layers
* @see LayeredLayout
*/ */
public void setLayers(Layers layers) { public void setLayers(Layers layers) {
Assert.notNull(layers, "Layers must not be null"); Assert.notNull(layers, "Layers must not be null");
this.layers = layers; this.layers = layers;
this.layersIndex = new LayersIndex(layers);
} }
/** /**
...@@ -165,12 +168,16 @@ public abstract class Packager { ...@@ -165,12 +168,16 @@ 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); WritableLibraries writeableLibraries = new WritableLibraries(libraries);
if (this.layers != null) {
writer = new LayerTrackingEntryWriter(writer);
}
writer.writeManifest(buildManifest(sourceJar)); writer.writeManifest(buildManifest(sourceJar));
writeLoaderClasses(writer); writeLoaderClasses(writer);
writer.writeEntries(sourceJar, getEntityTransformer(), writeableLibraries); writer.writeEntries(sourceJar, getEntityTransformer(), writeableLibraries);
writeableLibraries.write(writer); writeableLibraries.write(writer);
if (this.layers != null) {
writeLayerIndex(writer); writeLayerIndex(writer);
}
} }
private void writeLoaderClasses(AbstractJarWriter writer) throws IOException { private void writeLoaderClasses(AbstractJarWriter writer) throws IOException {
...@@ -184,19 +191,17 @@ public abstract class Packager { ...@@ -184,19 +191,17 @@ public abstract class Packager {
} }
private void writeLayerIndex(AbstractJarWriter writer) throws IOException { private void writeLayerIndex(AbstractJarWriter writer) throws IOException {
if (this.layers != null && getLayout() instanceof LayeredLayout) { String name = ((RepackagingLayout) this.layout).getLayersIndexFileLocation();
String location = ((LayeredLayout) this.layout).getLayersIndexFileLocation(); if (StringUtils.hasLength(name)) {
if (StringUtils.hasLength(location)) { Layer layer = this.layers.getLayer(name);
List<String> layerNames = new ArrayList<>(); this.layersIndex.add(layer, name);
this.layers.forEach((layer) -> layerNames.add(layer.toString())); writer.writeEntry(name, this.layersIndex::writeTo);
writer.writeIndexFile(location, layerNames);
}
} }
} }
private EntryTransformer getEntityTransformer() { private EntryTransformer getEntityTransformer() {
if (getLayout() instanceof RepackagingLayout) { if (getLayout() instanceof RepackagingLayout) {
return new RepackagingEntryTransformer((RepackagingLayout) getLayout(), this.layers); return new RepackagingEntryTransformer((RepackagingLayout) getLayout());
} }
return EntryTransformer.NONE; return EntryTransformer.NONE;
} }
...@@ -315,10 +320,7 @@ public abstract class Packager { ...@@ -315,10 +320,7 @@ public abstract class Packager {
private void addBootAttributes(Attributes attributes) { private void addBootAttributes(Attributes attributes) {
attributes.putValue(BOOT_VERSION_ATTRIBUTE, getClass().getPackage().getImplementationVersion()); attributes.putValue(BOOT_VERSION_ATTRIBUTE, getClass().getPackage().getImplementationVersion());
Layout layout = getLayout(); Layout layout = getLayout();
if (layout instanceof LayeredLayout) { if (layout instanceof RepackagingLayout) {
addBootBootAttributesForLayeredLayout(attributes, (LayeredLayout) layout);
}
else if (layout instanceof RepackagingLayout) {
addBootBootAttributesForRepackagingLayout(attributes, (RepackagingLayout) layout); addBootBootAttributesForRepackagingLayout(attributes, (RepackagingLayout) layout);
} }
else { else {
...@@ -326,15 +328,13 @@ public abstract class Packager { ...@@ -326,15 +328,13 @@ public abstract class Packager {
} }
} }
private void addBootBootAttributesForLayeredLayout(Attributes attributes, LayeredLayout layout) {
putIfHasLength(attributes, BOOT_LAYERS_INDEX_ATTRIBUTE, layout.getLayersIndexFileLocation());
putIfHasLength(attributes, BOOT_CLASSPATH_INDEX_ATTRIBUTE, layout.getClasspathIndexFileLocation());
}
private void addBootBootAttributesForRepackagingLayout(Attributes attributes, RepackagingLayout layout) { private void addBootBootAttributesForRepackagingLayout(Attributes attributes, RepackagingLayout layout) {
attributes.putValue(BOOT_CLASSES_ATTRIBUTE, layout.getRepackagedClassesLocation()); attributes.putValue(BOOT_CLASSES_ATTRIBUTE, layout.getRepackagedClassesLocation());
putIfHasLength(attributes, BOOT_LIB_ATTRIBUTE, getLayout().getLibraryLocation("", LibraryScope.COMPILE)); putIfHasLength(attributes, BOOT_LIB_ATTRIBUTE, getLayout().getLibraryLocation("", LibraryScope.COMPILE));
putIfHasLength(attributes, BOOT_CLASSPATH_INDEX_ATTRIBUTE, layout.getClasspathIndexFileLocation()); putIfHasLength(attributes, BOOT_CLASSPATH_INDEX_ATTRIBUTE, layout.getClasspathIndexFileLocation());
if (this.layers != null) {
putIfHasLength(attributes, BOOT_LAYERS_INDEX_ATTRIBUTE, layout.getLayersIndexFileLocation());
}
} }
private void addBootBootAttributesForPlainLayout(Attributes attributes, Layout layout) { private void addBootBootAttributesForPlainLayout(Attributes attributes, Layout layout) {
...@@ -371,11 +371,8 @@ public abstract class Packager { ...@@ -371,11 +371,8 @@ public abstract class Packager {
private final RepackagingLayout layout; private final RepackagingLayout layout;
private final Layers layers; private RepackagingEntryTransformer(RepackagingLayout layout) {
private RepackagingEntryTransformer(RepackagingLayout layout, Layers layers) {
this.layout = layout; this.layout = layout;
this.layers = layers;
} }
@Override @Override
...@@ -412,11 +409,6 @@ public abstract class Packager { ...@@ -412,11 +409,6 @@ public abstract class Packager {
} }
private String transformName(String name) { private String transformName(String name) {
if (this.layout instanceof LayeredLayout) {
Layer layer = this.layers.getLayer(name);
Assert.state(layer != null, () -> "Invalid 'null' layer from " + this.layers.getClass().getName());
return ((LayeredLayout) this.layout).getRepackagedClassesLocation(layer) + name;
}
return this.layout.getRepackagedClassesLocation() + name; return this.layout.getRepackagedClassesLocation() + name;
} }
...@@ -430,6 +422,35 @@ public abstract class Packager { ...@@ -430,6 +422,35 @@ public abstract class Packager {
} }
/**
* Decorator to track the layers as entries are written.
*/
private final class LayerTrackingEntryWriter extends AbstractJarWriter {
private final AbstractJarWriter writer;
private LayerTrackingEntryWriter(AbstractJarWriter writer) {
this.writer = writer;
}
@Override
public void writeNestedLibrary(String location, Library library) throws IOException {
this.writer.writeNestedLibrary(location, library);
Layer layer = Packager.this.layers.getLayer(library);
Packager.this.layersIndex.add(layer, location + library.getName());
}
@Override
protected void writeToArchive(ZipEntry entry, EntryWriter entryWriter) throws IOException {
this.writer.writeToArchive(entry, entryWriter);
if (!entry.getName().endsWith("/")) {
Layer layer = Packager.this.layers.getLayer(entry.getName());
Packager.this.layersIndex.add(layer, entry.getName());
}
}
}
/** /**
* 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.
...@@ -444,15 +465,13 @@ public abstract class Packager { ...@@ -444,15 +465,13 @@ public abstract class Packager {
addLibrary(library); addLibrary(library);
} }
}); });
if (Packager.this.includeRelevantJarModeJars) { if (Packager.this.layers != null && Packager.this.includeRelevantJarModeJars) {
if (getLayout() instanceof LayeredLayout) { addLibrary(JarModeLibrary.LAYER_TOOLS);
addLibrary(JarModeLibrary.LAYER_TOOLS);
}
} }
} }
private void addLibrary(Library library) { private void addLibrary(Library library) {
String location = getLocation(library); String location = getLayout().getLibraryLocation(library.getName(), library.getScope());
if (location != null) { if (location != null) {
String path = location + library.getName(); String path = location + library.getName();
Library existing = this.libraries.putIfAbsent(path, library); Library existing = this.libraries.putIfAbsent(path, library);
...@@ -460,17 +479,6 @@ public abstract class Packager { ...@@ -460,17 +479,6 @@ public abstract class Packager {
} }
} }
private String getLocation(Library library) {
Layout layout = getLayout();
if (layout instanceof LayeredLayout) {
Layers layers = Packager.this.layers;
Layer layer = layers.getLayer(library);
Assert.state(layer != null, () -> "Invalid 'null' library layer from " + layers.getClass().getName());
return ((LayeredLayout) layout).getLibraryLocation(library.getName(), library.getScope(), layer);
}
return layout.getLibraryLocation(library.getName(), library.getScope());
}
@Override @Override
public boolean requiresUnpack(String name) { public boolean requiresUnpack(String name) {
Library library = this.libraries.get(name); Library library = this.libraries.get(name);
...@@ -492,13 +500,19 @@ public abstract class Packager { ...@@ -492,13 +500,19 @@ public abstract class Packager {
writer.writeNestedLibrary(location, library); writer.writeNestedLibrary(location, library);
} }
if (getLayout() instanceof RepackagingLayout) { if (getLayout() instanceof RepackagingLayout) {
String location = ((RepackagingLayout) getLayout()).getClasspathIndexFileLocation(); writeClasspathIndex((RepackagingLayout) getLayout(), writer);
List<String> names = this.libraries.keySet().stream()
.map((key) -> key.substring(key.lastIndexOf('/') + 1)).collect(Collectors.toList());
writer.writeIndexFile(location, names);
} }
} }
private void writeClasspathIndex(RepackagingLayout layout, AbstractJarWriter writer) throws IOException {
List<String> names = this.libraries.keySet().stream().map(this::getJarName).collect(Collectors.toList());
writer.writeIndexFile(layout.getClasspathIndexFileLocation(), names);
}
private String getJarName(String path) {
return path.substring(path.lastIndexOf('/') + 1);
}
} }
} }
...@@ -42,4 +42,15 @@ public interface RepackagingLayout extends Layout { ...@@ -42,4 +42,15 @@ public interface RepackagingLayout extends Layout {
return null; return null;
} }
/**
* Returns the location of the layer index file that should be written or {@code null}
* if not index is required. The result should include the filename and is relative to
* the root of the jar.
* @return the layer index file location
* @since 2.3.0
*/
default String getLayersIndexFileLocation() {
return null;
}
} }
...@@ -238,7 +238,7 @@ abstract class AbstractPackagerTests<P extends Packager> { ...@@ -238,7 +238,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
} }
@Test @Test
void layeredLayout() throws Exception { void layersIndex() throws Exception {
TestJarFile libJar1 = new TestJarFile(this.tempDir); TestJarFile libJar1 = new TestJarFile(this.tempDir);
libJar1.addClass("a/b/C.class", ClassWithoutMainMethod.class, JAN_1_1985); libJar1.addClass("a/b/C.class", ClassWithoutMainMethod.class, JAN_1_1985);
File libJarFile1 = libJar1.getFile(); File libJarFile1 = libJar1.getFile();
...@@ -256,7 +256,6 @@ abstract class AbstractPackagerTests<P extends Packager> { ...@@ -256,7 +256,6 @@ abstract class AbstractPackagerTests<P extends Packager> {
layers.addLibrary(libJarFile3, "0003"); layers.addLibrary(libJarFile3, "0003");
packager.setLayers(layers); packager.setLayers(layers);
packager.setIncludeRelevantJarModeJars(false); packager.setIncludeRelevantJarModeJars(false);
packager.setLayout(new Layouts.LayeredJar());
execute(packager, (callback) -> { execute(packager, (callback) -> {
callback.library(new Library(libJarFile1, LibraryScope.COMPILE)); callback.library(new Library(libJarFile1, LibraryScope.COMPILE));
callback.library(new Library(libJarFile2, LibraryScope.COMPILE)); callback.library(new Library(libJarFile2, LibraryScope.COMPILE));
...@@ -264,36 +263,105 @@ abstract class AbstractPackagerTests<P extends Packager> { ...@@ -264,36 +263,105 @@ abstract class AbstractPackagerTests<P extends Packager> {
}); });
assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue(); assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue();
String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx"); String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx");
List<String> expectedJars = new ArrayList<>();
expectedJars.add("BOOT-INF/layers/0001/lib/" + libJarFile1.getName());
expectedJars.add("BOOT-INF/layers/0002/lib/" + libJarFile2.getName());
expectedJars.add("BOOT-INF/layers/0003/lib/" + libJarFile3.getName());
assertThat(Arrays.asList(classpathIndex.split("\\n"))).containsExactly(libJarFile1.getName(), assertThat(Arrays.asList(classpathIndex.split("\\n"))).containsExactly(libJarFile1.getName(),
libJarFile2.getName(), libJarFile3.getName()); libJarFile2.getName(), libJarFile3.getName());
assertThat(hasPackagedEntry("BOOT-INF/layers.idx")).isTrue(); assertThat(hasPackagedEntry("BOOT-INF/layers.idx")).isTrue();
String layersIndex = getPackagedEntryContent("BOOT-INF/layers.idx"); String layersIndex = getPackagedEntryContent("BOOT-INF/layers.idx");
List<String> expectedLayers = new ArrayList<>(); List<String> expectedLayers = new ArrayList<>();
expectedLayers.add("default"); getExpectedLayers(expectedLayers);
expectedLayers.add("0001"); expectedLayers.add("default " + "BOOT-INF/classpath.idx");
expectedLayers.add("0002"); expectedLayers.add("default " + "BOOT-INF/layers.idx");
expectedLayers.add("0003"); expectedLayers.add("0001 " + "BOOT-INF/lib/" + libJarFile1.getName());
expectedLayers.add("0002 " + "BOOT-INF/lib/" + libJarFile2.getName());
expectedLayers.add("0003 " + "BOOT-INF/lib/" + libJarFile3.getName());
assertThat(Arrays.asList(layersIndex.split("\\n"))).containsExactly(expectedLayers.toArray(new String[0])); assertThat(Arrays.asList(layersIndex.split("\\n"))).containsExactly(expectedLayers.toArray(new String[0]));
} }
@Test private void getExpectedLayers(List<String> expectedLayers) {
void layeredLayoutAddJarModeJar() throws Exception { expectedLayers.add("default META-INF/MANIFEST.MF");
expectedLayers.add("default org/springframework/boot/loader/ClassPathIndexFile.class");
expectedLayers.add("default org/springframework/boot/loader/ExecutableArchiveLauncher.class");
expectedLayers.add("default org/springframework/boot/loader/JarLauncher.class");
expectedLayers.add(
"default org/springframework/boot/loader/LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class");
expectedLayers.add("default org/springframework/boot/loader/LaunchedURLClassLoader.class");
expectedLayers.add("default org/springframework/boot/loader/Launcher.class");
expectedLayers.add("default org/springframework/boot/loader/MainMethodRunner.class");
expectedLayers.add("default org/springframework/boot/loader/PropertiesLauncher$1.class");
expectedLayers.add("default org/springframework/boot/loader/PropertiesLauncher$ArchiveEntryFilter.class");
expectedLayers
.add("default org/springframework/boot/loader/PropertiesLauncher$PrefixMatchingArchiveFilter.class");
expectedLayers.add("default org/springframework/boot/loader/PropertiesLauncher.class");
expectedLayers.add("default org/springframework/boot/loader/WarLauncher.class");
expectedLayers.add("default org/springframework/boot/loader/archive/Archive$Entry.class");
expectedLayers.add("default org/springframework/boot/loader/archive/Archive$EntryFilter.class");
expectedLayers.add("default org/springframework/boot/loader/archive/Archive.class");
expectedLayers.add("default org/springframework/boot/loader/archive/ExplodedArchive$AbstractIterator.class");
expectedLayers.add("default org/springframework/boot/loader/archive/ExplodedArchive$ArchiveIterator.class");
expectedLayers.add("default org/springframework/boot/loader/archive/ExplodedArchive$EntryIterator.class");
expectedLayers.add("default org/springframework/boot/loader/archive/ExplodedArchive$FileEntry.class");
expectedLayers
.add("default org/springframework/boot/loader/archive/ExplodedArchive$SimpleJarFileArchive.class");
expectedLayers.add("default org/springframework/boot/loader/archive/ExplodedArchive.class");
expectedLayers.add("default org/springframework/boot/loader/archive/JarFileArchive$AbstractIterator.class");
expectedLayers.add("default org/springframework/boot/loader/archive/JarFileArchive$EntryIterator.class");
expectedLayers.add("default org/springframework/boot/loader/archive/JarFileArchive$JarFileEntry.class");
expectedLayers
.add("default org/springframework/boot/loader/archive/JarFileArchive$NestedArchiveIterator.class");
expectedLayers.add("default org/springframework/boot/loader/archive/JarFileArchive.class");
expectedLayers.add("default org/springframework/boot/loader/data/RandomAccessData.class");
expectedLayers.add("default org/springframework/boot/loader/data/RandomAccessDataFile$1.class");
expectedLayers.add("default org/springframework/boot/loader/data/RandomAccessDataFile$DataInputStream.class");
expectedLayers.add("default org/springframework/boot/loader/data/RandomAccessDataFile$FileAccess.class");
expectedLayers.add("default org/springframework/boot/loader/data/RandomAccessDataFile.class");
expectedLayers.add("default org/springframework/boot/loader/jar/AsciiBytes.class");
expectedLayers.add("default org/springframework/boot/loader/jar/Bytes.class");
expectedLayers.add("default org/springframework/boot/loader/jar/CentralDirectoryEndRecord$1.class");
expectedLayers.add("default org/springframework/boot/loader/jar/CentralDirectoryEndRecord$Zip64End.class");
expectedLayers.add("default org/springframework/boot/loader/jar/CentralDirectoryEndRecord$Zip64Locator.class");
expectedLayers.add("default org/springframework/boot/loader/jar/CentralDirectoryEndRecord.class");
expectedLayers.add("default org/springframework/boot/loader/jar/CentralDirectoryFileHeader.class");
expectedLayers.add("default org/springframework/boot/loader/jar/CentralDirectoryParser.class");
expectedLayers.add("default org/springframework/boot/loader/jar/CentralDirectoryVisitor.class");
expectedLayers.add("default org/springframework/boot/loader/jar/FileHeader.class");
expectedLayers.add("default org/springframework/boot/loader/jar/Handler.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarEntry.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarEntryFilter.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarFile$1.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarFile$2.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarFile$JarFileType.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarFile.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarFileEntries$1.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarFileEntries$EntryIterator.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarFileEntries.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarURLConnection$1.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarURLConnection$2.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarURLConnection$CloseAction.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarURLConnection$JarEntryName.class");
expectedLayers.add("default org/springframework/boot/loader/jar/JarURLConnection.class");
expectedLayers.add("default org/springframework/boot/loader/jar/StringSequence.class");
expectedLayers.add("default org/springframework/boot/loader/jar/ZipInflaterInputStream.class");
expectedLayers.add("default org/springframework/boot/loader/jarmode/JarMode.class");
expectedLayers.add("default org/springframework/boot/loader/jarmode/JarModeLauncher.class");
expectedLayers.add("default org/springframework/boot/loader/jarmode/TestJarMode.class");
expectedLayers.add("default org/springframework/boot/loader/util/SystemPropertyUtils.class");
expectedLayers.add("default BOOT-INF/classes/a/b/C.class");
}
@Test
void layersEnabledAddJarModeJar() throws Exception {
this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class); this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class);
P packager = createPackager(); P packager = createPackager();
TestLayers layers = new TestLayers(); TestLayers layers = new TestLayers();
packager.setLayers(layers); packager.setLayers(layers);
packager.setLayout(new Layouts.LayeredJar());
execute(packager, Libraries.NONE); execute(packager, Libraries.NONE);
assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue(); assertThat(hasPackagedEntry("BOOT-INF/classpath.idx")).isTrue();
String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx"); String classpathIndex = getPackagedEntryContent("BOOT-INF/classpath.idx");
assertThat(Arrays.asList(classpathIndex.split("\\n"))).containsExactly("spring-boot-jarmode-layertools.jar"); assertThat(Arrays.asList(classpathIndex.split("\\n"))).containsExactly("spring-boot-jarmode-layertools.jar");
assertThat(hasPackagedEntry("BOOT-INF/layers.idx")).isTrue(); assertThat(hasPackagedEntry("BOOT-INF/layers.idx")).isTrue();
String layersIndex = getPackagedEntryContent("BOOT-INF/layers.idx"); String layersIndex = getPackagedEntryContent("BOOT-INF/layers.idx");
assertThat(Arrays.asList(layersIndex.split("\\n"))).containsExactly("default"); assertThat(Arrays.stream(layersIndex.split("\\n")).map((n) -> n.split(" ")[0]).distinct())
.containsExactly("default");
} }
@Test @Test
......
...@@ -60,6 +60,14 @@ class ImplicitLayerResolverTests { ...@@ -60,6 +60,14 @@ class ImplicitLayerResolverTests {
assertThat(this.layers.getLayer("com/example/application.properties")).isEqualTo(StandardLayers.APPLICATION); assertThat(this.layers.getLayer("com/example/application.properties")).isEqualTo(StandardLayers.APPLICATION);
} }
@Test
void getLayerWhenLoaderClassReturnsLoaderLayer() {
assertThat(this.layers.getLayer("org/springframework/boot/loader/Launcher.class"))
.isEqualTo(StandardLayers.SPRING_BOOT_LOADER);
assertThat(this.layers.getLayer("org/springframework/boot/loader/Utils.class"))
.isEqualTo(StandardLayers.SPRING_BOOT_LOADER);
}
@Test @Test
void getLayerWhenLibraryIsSnapshotReturnsSnapshotLayer() { void getLayerWhenLibraryIsSnapshotReturnsSnapshotLayer() {
assertThat(this.layers.getLayer(mockLibrary("spring-boot.2.0.0.BUILD-SNAPSHOT.jar"))) assertThat(this.layers.getLayer(mockLibrary("spring-boot.2.0.0.BUILD-SNAPSHOT.jar")))
......
...@@ -19,7 +19,6 @@ package org.springframework.boot.loader; ...@@ -19,7 +19,6 @@ package org.springframework.boot.loader;
import java.io.IOException; import java.io.IOException;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import java.util.regex.Pattern;
import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.archive.Archive.EntryFilter; import org.springframework.boot.loader.archive.Archive.EntryFilter;
...@@ -37,17 +36,13 @@ import org.springframework.boot.loader.archive.ExplodedArchive; ...@@ -37,17 +36,13 @@ import org.springframework.boot.loader.archive.ExplodedArchive;
*/ */
public class JarLauncher extends ExecutableArchiveLauncher { public class JarLauncher extends ExecutableArchiveLauncher {
private static final Pattern CLASSES_PATTERN = Pattern.compile("BOOT-INF\\/(layers\\/.*\\/)?classes/");
private static final Pattern LIBS_PATTERN = Pattern.compile("BOOT-INF\\/(layers\\/.*\\/)?lib\\/.+");
private static final String DEFAULT_CLASSPATH_INDEX_LOCATION = "BOOT-INF/classpath.idx"; private static final String DEFAULT_CLASSPATH_INDEX_LOCATION = "BOOT-INF/classpath.idx";
static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> { static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
if (entry.isDirectory()) { if (entry.isDirectory()) {
return CLASSES_PATTERN.matcher(entry.getName()).matches(); return entry.getName().equals("BOOT-INF/classes/");
} }
return LIBS_PATTERN.matcher(entry.getName()).matches(); return entry.getName().startsWith("BOOT-INF/lib/");
}; };
public JarLauncher() { public JarLauncher() {
......
...@@ -26,6 +26,8 @@ import org.assertj.core.api.AssertProvider; ...@@ -26,6 +26,8 @@ import org.assertj.core.api.AssertProvider;
import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.maven.MavenBuild.ProjectCallback;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
...@@ -77,11 +79,11 @@ public class BuildInfoIntegrationTests { ...@@ -77,11 +79,11 @@ public class BuildInfoIntegrationTests {
.doesNotContainBuildTime())); .doesNotContainBuildTime()));
} }
private Consumer<File> buildInfo(Consumer<AssertProvider<BuildInfoAssert>> buildInfo) { private ProjectCallback buildInfo(Consumer<AssertProvider<BuildInfoAssert>> buildInfo) {
return buildInfo("target/classes/META-INF/build-info.properties", buildInfo); return buildInfo("target/classes/META-INF/build-info.properties", buildInfo);
} }
private Consumer<File> buildInfo(String location, Consumer<AssertProvider<BuildInfoAssert>> buildInfo) { private ProjectCallback buildInfo(String location, Consumer<AssertProvider<BuildInfoAssert>> buildInfo) {
return (project) -> buildInfo.accept((buildInfo(project, location))); return (project) -> buildInfo.accept((buildInfo(project, location)));
} }
......
...@@ -17,16 +17,21 @@ package org.springframework.boot.maven; ...@@ -17,16 +17,21 @@ package org.springframework.boot.maven;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.loader.tools.FileUtils; import org.springframework.boot.loader.tools.FileUtils;
import org.springframework.boot.loader.tools.JarModeLibrary; import org.springframework.boot.loader.tools.JarModeLibrary;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils; import org.springframework.util.FileSystemUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -291,35 +296,54 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests { ...@@ -291,35 +296,54 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
} }
@TestTemplate @TestTemplate
void whenJarIsRepackagedWithTheLayeredLayoutTheJarContainsLayers(MavenBuild mavenBuild) { void whenJarIsRepackagedWithLayersEnabledTheJarContainsTheLayersIndex(MavenBuild mavenBuild) {
mavenBuild.project("jar-layered").execute((project) -> { mavenBuild.project("jar-layered").execute((project) -> {
File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar"); File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/layers/application/classes/") assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
.hasEntryWithNameStartingWith("BOOT-INF/layers/dependencies/lib/jar-release") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-release")
.hasEntryWithNameStartingWith("BOOT-INF/layers/snapshot-dependencies/lib/jar-snapshot") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-snapshot").hasEntryWithNameStartingWith(
.hasEntryWithNameStartingWith(jarModeLayerTools()); "BOOT-INF/lib/" + JarModeLibrary.LAYER_TOOLS.getCoordinates().getArtifactId());
try {
try (JarFile jarFile = new JarFile(repackaged)) {
ZipEntry entry = jarFile.getEntry("BOOT-INF/layers.idx");
InputStream inputStream = jarFile.getInputStream(entry);
InputStreamReader reader = new InputStreamReader(inputStream);
String[] lines = FileCopyUtils.copyToString(reader).split("\\n");
assertThat(Arrays.stream(lines).map((n) -> n.split(" ")[0]).distinct()).containsExactly(
"dependencies", "spring-boot-loader", "snapshot-dependencies", "application");
}
}
catch (IOException ex) {
}
}); });
} }
@TestTemplate @TestTemplate
void whenJarIsRepackagedWithTheLayeredLayoutAndLayerToolsExcluded(MavenBuild mavenBuild) { void whenJarIsRepackagedWithTheLayersEnabledAndLayerToolsExcluded(MavenBuild mavenBuild) {
mavenBuild.project("jar-layered-no-layer-tools").execute((project) -> { mavenBuild.project("jar-layered-no-layer-tools").execute((project) -> {
File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar"); File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/layers/application/classes/") assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
.hasEntryWithNameStartingWith("BOOT-INF/layers/dependencies/lib/jar-release") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-release")
.hasEntryWithNameStartingWith("BOOT-INF/layers/snapshot-dependencies/lib/jar-snapshot") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-snapshot")
.doesNotHaveEntryWithNameStartingWith(jarModeLayerTools()); .doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/" + JarModeLibrary.LAYER_TOOLS.getName());
}); });
} }
@TestTemplate @TestTemplate
void whenJarIsRepackagedWithTheCustomLayeredLayout(MavenBuild mavenBuild) { void whenJarIsRepackagedWithTheCustomLayers(MavenBuild mavenBuild) {
mavenBuild.project("jar-layered-custom").execute((project) -> { mavenBuild.project("jar-layered-custom").execute((project) -> {
File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar"); File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/layers/application/classes/") assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
.hasEntryWithNameStartingWith("BOOT-INF/layers/my-dependencies-name/lib/jar-release") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-release")
.hasEntryWithNameStartingWith("BOOT-INF/layers/snapshot-dependencies/lib/jar-snapshot") .hasEntryWithNameStartingWith("BOOT-INF/lib/jar-snapshot");
.hasEntryWithNameStartingWith("BOOT-INF/layers/configuration/classes/application.yml"); try (JarFile jarFile = new JarFile(repackaged)) {
ZipEntry entry = jarFile.getEntry("BOOT-INF/layers.idx");
InputStream inputStream = jarFile.getInputStream(entry);
InputStreamReader reader = new InputStreamReader(inputStream);
String[] lines = FileCopyUtils.copyToString(reader).split("\\n");
assertThat(Arrays.stream(lines).map((n) -> n.split(" ")[0]).distinct()).containsExactly(
"my-dependencies-name", "snapshot-dependencies", "configuration", "application");
}
}); });
} }
...@@ -332,13 +356,6 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests { ...@@ -332,13 +356,6 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
assertThat(firstHash).isEqualTo(secondHash); assertThat(firstHash).isEqualTo(secondHash);
} }
private String jarModeLayerTools() {
JarModeLibrary library = JarModeLibrary.LAYER_TOOLS;
String version = library.getCoordinates().getVersion();
String layer = (version == null || !version.contains("SNAPSHOT")) ? "dependencies" : "snapshot-dependencies";
return "BOOT-INF/layers/" + layer + "/lib/" + library.getName();
}
private String buildJarWithOutputTimestamp(MavenBuild mavenBuild) { private String buildJarWithOutputTimestamp(MavenBuild mavenBuild) {
AtomicReference<String> jarHash = new AtomicReference<>(); AtomicReference<String> jarHash = new AtomicReference<>();
mavenBuild.project("jar-output-timestamp").execute((project) -> { mavenBuild.project("jar-output-timestamp").execute((project) -> {
......
...@@ -39,7 +39,6 @@ import java.util.List; ...@@ -39,7 +39,6 @@ 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.Properties; import java.util.Properties;
import java.util.function.Consumer;
import org.apache.maven.shared.invoker.DefaultInvocationRequest; import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker; import org.apache.maven.shared.invoker.DefaultInvoker;
...@@ -56,7 +55,6 @@ import static org.assertj.core.api.Assertions.contentOf; ...@@ -56,7 +55,6 @@ import static org.assertj.core.api.Assertions.contentOf;
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Scott Frederick * @author Scott Frederick
*
*/ */
class MavenBuild { class MavenBuild {
...@@ -70,7 +68,7 @@ class MavenBuild { ...@@ -70,7 +68,7 @@ class MavenBuild {
private final Properties properties = new Properties(); private final Properties properties = new Properties();
private Consumer<File> preparation; private ProjectCallback preparation;
private File projectDir; private File projectDir;
...@@ -111,20 +109,20 @@ class MavenBuild { ...@@ -111,20 +109,20 @@ class MavenBuild {
return this; return this;
} }
MavenBuild prepare(Consumer<File> callback) { MavenBuild prepare(ProjectCallback callback) {
this.preparation = callback; this.preparation = callback;
return this; return this;
} }
void execute(Consumer<File> callback) { void execute(ProjectCallback callback) {
execute(callback, 0); execute(callback, 0);
} }
void executeAndFail(Consumer<File> callback) { void executeAndFail(ProjectCallback callback) {
execute(callback, 1); execute(callback, 1);
} }
private void execute(Consumer<File> callback, int expectedExitCode) { private void execute(ProjectCallback callback, int expectedExitCode) {
Invoker invoker = new DefaultInvoker(); Invoker invoker = new DefaultInvoker();
invoker.setMavenHome(this.home); invoker.setMavenHome(this.home);
InvocationRequest request = new DefaultInvocationRequest(); InvocationRequest request = new DefaultInvocationRequest();
...@@ -175,7 +173,7 @@ class MavenBuild { ...@@ -175,7 +173,7 @@ class MavenBuild {
File target = new File(this.temp, "target"); File target = new File(this.temp, "target");
target.mkdirs(); target.mkdirs();
if (this.preparation != null) { if (this.preparation != null) {
this.preparation.accept(this.temp); this.preparation.doWith(this.temp);
} }
File buildLogFile = new File(target, "build.log"); File buildLogFile = new File(target, "build.log");
try (PrintWriter buildLog = new PrintWriter(new FileWriter(buildLogFile))) { try (PrintWriter buildLog = new PrintWriter(new FileWriter(buildLogFile))) {
...@@ -191,7 +189,7 @@ class MavenBuild { ...@@ -191,7 +189,7 @@ class MavenBuild {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
callback.accept(this.temp); callback.doWith(this.temp);
} }
catch (Exception ex) { catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
...@@ -213,4 +211,19 @@ class MavenBuild { ...@@ -213,4 +211,19 @@ class MavenBuild {
} }
} }
/**
* Action to take on a maven project folder.
*/
@FunctionalInterface
public interface ProjectCallback {
/**
* Take the action on the given project.
* @param project the project directory
* @throws Exception on error
*/
void doWith(File project) throws Exception;
}
} }
...@@ -15,9 +15,9 @@ ...@@ -15,9 +15,9 @@
<into layer="my-dependencies-name" /> <into layer="my-dependencies-name" />
</dependencies> </dependencies>
<layerOrder> <layerOrder>
<layer>my-dependencies-name</layer>
<layer>snapshot-dependencies</layer>
<layer>configuration</layer> <layer>configuration</layer>
<layer>application</layer> <layer>application</layer>
<layer>snapshot-dependencies</layer>
<layer>my-dependencies-name</layer>
</layerOrder> </layerOrder>
</layers> </layers>
\ No newline at end of file
...@@ -43,7 +43,6 @@ import org.springframework.boot.loader.tools.Layout; ...@@ -43,7 +43,6 @@ import org.springframework.boot.loader.tools.Layout;
import org.springframework.boot.loader.tools.LayoutFactory; import org.springframework.boot.loader.tools.LayoutFactory;
import org.springframework.boot.loader.tools.Layouts.Expanded; import org.springframework.boot.loader.tools.Layouts.Expanded;
import org.springframework.boot.loader.tools.Layouts.Jar; import org.springframework.boot.loader.tools.Layouts.Jar;
import org.springframework.boot.loader.tools.Layouts.LayeredJar;
import org.springframework.boot.loader.tools.Layouts.None; import org.springframework.boot.loader.tools.Layouts.None;
import org.springframework.boot.loader.tools.Layouts.War; import org.springframework.boot.loader.tools.Layouts.War;
import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Libraries;
...@@ -58,6 +57,8 @@ import org.springframework.boot.loader.tools.layer.CustomLayers; ...@@ -58,6 +57,8 @@ import org.springframework.boot.loader.tools.layer.CustomLayers;
*/ */
public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo { public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo {
private static final org.springframework.boot.loader.tools.Layers IMPLICIT_LAYERS = org.springframework.boot.loader.tools.Layers.IMPLICIT;
/** /**
* The Maven project. * The Maven project.
* @since 1.0.0 * @since 1.0.0
...@@ -135,10 +136,8 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo ...@@ -135,10 +136,8 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
packager.setLayout(this.layout.layout()); packager.setLayout(this.layout.layout());
} }
if (this.layers != null && this.layers.isEnabled()) { if (this.layers != null && this.layers.isEnabled()) {
if (this.layers.getConfiguration() != null) { packager.setLayers((this.layers.getConfiguration() != null)
packager.setLayers(getCustomLayers(this.layers.getConfiguration())); ? getCustomLayers(this.layers.getConfiguration()) : IMPLICIT_LAYERS);
}
packager.setLayout(new LayeredJar());
packager.setIncludeRelevantJarModeJars(this.layers.isIncludeLayerTools()); packager.setIncludeRelevantJarModeJars(this.layers.isIncludeLayerTools());
} }
return packager; return packager;
......
...@@ -33,8 +33,8 @@ public class Layers { ...@@ -33,8 +33,8 @@ public class Layers {
private File configuration; private File configuration;
/** /**
* Whether layered jar layout is enabled. * Whether a layers.idx file should be added to the jar.
* @return true if the layered layout is enabled. * @return true if a layers.idx file should be added.
*/ */
public boolean isEnabled() { public boolean isEnabled() {
return this.enabled; return this.enabled;
......
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