Add support for creating layered war files with Maven

See gh-22821
This commit is contained in:
Madhura Bhave
2021-01-28 12:39:19 -08:00
parent fd27c26e3b
commit 152698f2b2
47 changed files with 902 additions and 128 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -30,6 +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.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
@@ -90,11 +91,11 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
* @throws IOException if the entries cannot be written
*/
public void writeEntries(JarFile jarFile) throws IOException {
writeEntries(jarFile, EntryTransformer.NONE, UnpackHandler.NEVER);
writeEntries(jarFile, EntryTransformer.NONE, UnpackHandler.NEVER, (name) -> false);
}
final void writeEntries(JarFile jarFile, EntryTransformer entryTransformer, UnpackHandler unpackHandler)
throws IOException {
final void writeEntries(JarFile jarFile, EntryTransformer entryTransformer, UnpackHandler unpackHandler,
Predicate<String> libraryPredicate) throws IOException {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarArchiveEntry entry = new JarArchiveEntry(entries.nextElement());
@@ -103,7 +104,8 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
EntryWriter entryWriter = new InputStreamEntryWriter(inputStream);
JarArchiveEntry transformedEntry = entryTransformer.transform(entry);
if (transformedEntry != null) {
writeEntry(transformedEntry, entryWriter, unpackHandler, true);
boolean updateLayerIndex = !libraryPredicate.test(entry.getName());
writeEntry(transformedEntry, entryWriter, unpackHandler, updateLayerIndex);
}
}
}

View File

@@ -50,6 +50,28 @@ public interface Layout {
*/
String getClassesLocation();
/**
* Returns the location of the classpath 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 classpath index file location
* @since 2.5.0
*/
default String getClasspathIndexFileLocation() {
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.5.0
*/
default String getLayersIndexFileLocation() {
return null;
}
/**
* Returns if loader classes should be included to make the archive executable.
* @return if the layout is executable

View File

@@ -160,6 +160,16 @@ public final class Layouts {
return "WEB-INF/classes/";
}
@Override
public String getClasspathIndexFileLocation() {
return "WEB-INF/classpath.idx";
}
@Override
public String getLayersIndexFileLocation() {
return "WEB-INF/layers.idx";
}
@Override
public boolean isExecutable() {
return true;

View File

@@ -172,7 +172,7 @@ public abstract class Packager {
}
writer.writeManifest(buildManifest(sourceJar));
writeLoaderClasses(writer);
writer.writeEntries(sourceJar, getEntityTransformer(), writeableLibraries);
writer.writeEntries(sourceJar, getEntityTransformer(), writeableLibraries, writeableLibraries::containsEntry);
writeableLibraries.write(writer);
if (isLayered()) {
writeLayerIndex(writer);
@@ -190,7 +190,7 @@ public abstract class Packager {
}
private void writeLayerIndex(AbstractJarWriter writer) throws IOException {
String name = ((RepackagingLayout) this.layout).getLayersIndexFileLocation();
String name = this.layout.getLayersIndexFileLocation();
if (StringUtils.hasLength(name)) {
Layer layer = this.layers.getLayer(name);
this.layersIndex.add(layer, name);
@@ -318,17 +318,17 @@ public abstract class Packager {
private void addBootAttributes(Attributes attributes) {
attributes.putValue(BOOT_VERSION_ATTRIBUTE, getClass().getPackage().getImplementationVersion());
Layout layout = getLayout();
if (layout instanceof RepackagingLayout) {
addBootBootAttributesForRepackagingLayout(attributes, (RepackagingLayout) layout);
}
else {
addBootBootAttributesForPlainLayout(attributes);
}
addBootAttributesForLayout(attributes);
}
private void addBootBootAttributesForRepackagingLayout(Attributes attributes, RepackagingLayout layout) {
attributes.putValue(BOOT_CLASSES_ATTRIBUTE, layout.getRepackagedClassesLocation());
private void addBootAttributesForLayout(Attributes attributes) {
Layout layout = getLayout();
if (layout instanceof RepackagingLayout) {
attributes.putValue(BOOT_CLASSES_ATTRIBUTE, ((RepackagingLayout) layout).getRepackagedClassesLocation());
}
else {
attributes.putValue(BOOT_CLASSES_ATTRIBUTE, layout.getClassesLocation());
}
putIfHasLength(attributes, BOOT_LIB_ATTRIBUTE, getLayout().getLibraryLocation("", LibraryScope.COMPILE));
putIfHasLength(attributes, BOOT_CLASSPATH_INDEX_ATTRIBUTE, layout.getClasspathIndexFileLocation());
if (isLayered()) {
@@ -336,11 +336,6 @@ public abstract class Packager {
}
}
private void addBootBootAttributesForPlainLayout(Attributes attributes) {
attributes.putValue(BOOT_CLASSES_ATTRIBUTE, getLayout().getClassesLocation());
putIfHasLength(attributes, BOOT_LIB_ATTRIBUTE, getLayout().getLibraryLocation("", LibraryScope.COMPILE));
}
private void putIfHasLength(Attributes attributes, String name, String value) {
if (StringUtils.hasLength(value)) {
attributes.putValue(name, value);
@@ -348,7 +343,7 @@ public abstract class Packager {
}
private boolean isLayered() {
return this.layers != null && getLayout() instanceof Layouts.Jar;
return this.layers != null;
}
/**
@@ -466,6 +461,10 @@ public abstract class Packager {
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();
@@ -473,12 +472,12 @@ public abstract class Packager {
String location = path.substring(0, path.lastIndexOf('/') + 1);
writer.writeNestedLibrary(location, library);
}
if (getLayout() instanceof RepackagingLayout) {
writeClasspathIndex((RepackagingLayout) getLayout(), writer);
if (Packager.this.layout instanceof RepackagingLayout) {
writeClasspathIndex(getLayout(), writer);
}
}
private void writeClasspathIndex(RepackagingLayout layout, AbstractJarWriter writer) throws IOException {
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);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -31,26 +31,4 @@ public interface RepackagingLayout extends Layout {
*/
String getRepackagedClassesLocation();
/**
* Returns the location of the classpath 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 classpath index file location
* @since 2.3.0
*/
default String getClasspathIndexFileLocation() {
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;
}
}