Skip to content

Commit

Permalink
Polish new layered jar support
Browse files Browse the repository at this point in the history
  • Loading branch information
wilkinsona committed Apr 3, 2020
1 parent 3e936dd commit 34e6026
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ bootJar {
}
intoLayer("dependencies")
}
layerOrder "dependencies", "spring-boot-loader", "snapshot-dependencies", "application"
layerOrder = ["dependencies", "spring-boot-loader", "snapshot-dependencies", "application"]
}
}
// end::layered[]
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ plugins {
id("org.springframework.boot") version "{version}"
}

tasks.getByName<BootJar>("bootJar") {
mainClassName = "com.example.ExampleApplication"
}

// tag::layered[]
tasks.getByName<BootJar>("bootJar") {
layered {
Expand All @@ -18,9 +22,9 @@ tasks.getByName<BootJar>("bootJar") {
intoLayer("snapshot-dependencies") {
include("*:*:*SNAPSHOT")
}
intoLayer("dependencies") {
intoLayer("dependencies")
}
layersOrder("dependencies", "spring-boot-loader", "snapshot-dependencies", "application")
layerOrder = listOf("dependencies", "spring-boot-loader", "snapshot-dependencies", "application")
}
}
// end::layered[]
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ bootJar {

// tag::layered[]
bootJar {
layers {
layered {
includeLayerTools = false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ tasks.getByName<BootJar>("bootJar") {

// tag::layered[]
tasks.getByName<BootJar>("bootJar") {
layers {
includeLayerTools = false
layered {
isIncludeLayerTools = false
}
}
// end::layered[]
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.util.Collections;
import java.util.concurrent.Callable;

import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.CopySpec;
Expand All @@ -33,7 +32,6 @@
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.util.ConfigureUtil;

/**
* A custom {@link Jar} task that produces a Spring Boot executable jar.
Expand Down Expand Up @@ -158,28 +156,33 @@ public void launchScript(Action<LaunchScriptConfiguration> action) {
action.execute(enableLaunchScriptIfNecessary());
}

/**
* Returns the spec that describes the layers in a layerd jar.
* @return the spec for the layers or {@code null}.
* @since 2.3.0
*/
@Nested
@Optional
public LayeredSpec getLayered() {
return this.layered;
}

/**
* Configures the jar to be layered using the default layering.
* @since 2.3.0
*/
public void layered() {
layered(true);
}

public void layered(boolean layered) {
this.layered = layered ? new LayeredSpec() : null;
}

public void layered(Closure<?> closure) {
layered(ConfigureUtil.configureUsing(closure));
enableLayeringIfNecessary();
}

/**
* Configures the jar to be layered, customizing the layers using the given
* {@code action}.
* @param action the action to apply
* @since 2.3.0
*/
public void layered(Action<LayeredSpec> action) {
LayeredSpec layered = new LayeredSpec();
action.execute(layered);
this.layered = layered;
action.execute(enableLayeringIfNecessary());
}

@Override
Expand Down Expand Up @@ -258,6 +261,7 @@ protected ZipCompression resolveZipCompression(FileCopyDetails details) {
* {@code BOOT-INF/lib} is considered to be a library.
* @param details the file copy details
* @return {@code true} if the details are for a library
* @since 2.3.0
*/
protected boolean isLibrary(FileCopyDetails details) {
String path = details.getRelativePath().getPathString();
Expand All @@ -273,6 +277,13 @@ private LaunchScriptConfiguration enableLaunchScriptIfNecessary() {
return launchScript;
}

private LayeredSpec enableLayeringIfNecessary() {
if (this.layered == null) {
this.layered = new LayeredSpec();
}
return this.layered;
}

/**
* Syntactic sugar that makes {@link CopySpec#into} calls a little easier to read.
* @param <T> the result type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,59 +61,117 @@ public class LayeredSpec {

private Layers layers;

/**
* Returns whether the layer tools should be included as a dependency in the layered
* jar.
* @return whether the layer tools should be included
*/
@Input
public boolean isIncludeLayerTools() {
return this.includeLayerTools;
}

/**
* Sets whether the layer tools should be included as a dependency in the layered jar.
* @param includeLayerTools {@code true} if the layer tools should be included,
* otherwise {@code false}
*/
public void setIncludeLayerTools(boolean includeLayerTools) {
this.includeLayerTools = includeLayerTools;
}

/**
* Returns the {@link ApplicationSpec} that controls the layers to which application
* classes and resources belong.
* @return the application spec
*/
@Input
public ApplicationSpec getApplication() {
return this.application;
}

public void application(ApplicationSpec spec) {
/**
* Sets the {@link ApplicationSpec} that controls the layers to which application
* classes are resources belong.
* @param spec the application spec
*/
public void setApplication(ApplicationSpec spec) {
this.application = spec;
}

public void application(Closure<?> closure) {
application(ConfigureUtil.configureUsing(closure));
}

/**
* Customizes the {@link ApplicationSpec} using the given {@code action}.
* @param action the action
*/
public void application(Action<ApplicationSpec> action) {
action.execute(this.application);
}

/**
* Customizes the {@link ApplicationSpec} using the given {@code closure}.
* @param closure the closure
*/
public void application(Closure<?> closure) {
application(ConfigureUtil.configureUsing(closure));
}

/**
* Returns the {@link DependenciesSpec} that controls the layers to which dependencies
* belong.
* @return the dependencies spec
*/
@Input
public DependenciesSpec getDependencies() {
return this.dependencies;
}

public void dependencies(DependenciesSpec spec) {
/**
* Sets the {@link DependenciesSpec} that controls the layers to which dependencies
* belong.
* @param spec the dependencies spec
*/
public void setDependencies(DependenciesSpec spec) {
this.dependencies = spec;
}

public void dependencies(Closure<?> closure) {
dependencies(ConfigureUtil.configureUsing(closure));
}

/**
* Customizes the {@link DependenciesSpec} using the given {@code action}.
* @param action the action
*/
public void dependencies(Action<DependenciesSpec> action) {
action.execute(this.dependencies);
}

/**
* Customizes the {@link DependenciesSpec} using the given {@code closure}.
* @param closure the closure
*/
public void dependencies(Closure<?> closure) {
dependencies(ConfigureUtil.configureUsing(closure));
}

/**
* Returns the order of the layers in the jar from least to most frequently changing.
* @return the layer order
*/
@Input
public List<String> getLayerOrder() {
return this.layerOrder;
}

public void layerOrder(String... layerOrder) {
/**
* Sets to order of the layers in the jar from least to most frequently changing.
* @param layerOrder the layer order
*/
public void setLayerOrder(String... layerOrder) {
this.layerOrder = Arrays.asList(layerOrder);
}

public void layerOrder(List<String> layerOrder) {
/**
* Sets to order of the layers in the jar from least to most frequently changing.
* @param layerOrder the layer order
*/
public void setLayerOrder(List<String> layerOrder) {
this.layerOrder = layerOrder;
}

Expand Down Expand Up @@ -141,6 +199,10 @@ private Layers createLayers() {
return new CustomLayers(layers, this.application.asSelectors(), this.dependencies.asSelectors());
}

/**
* Base class for specs that control the layers to which a category of content should
* belong.
*/
public abstract static class IntoLayersSpec implements Serializable {

private final List<IntoLayerSpec> intoLayers;
Expand Down Expand Up @@ -174,6 +236,9 @@ <T> List<ContentSelector<T>> asSelectors(Function<String, ContentFilter<T>> filt

}

/**
* Spec that controls the content that should be part of a particular layer.
*/
public static class IntoLayerSpec implements Serializable {

private final String intoLayer;
Expand All @@ -182,14 +247,33 @@ public static class IntoLayerSpec implements Serializable {

private final List<String> excludes = new ArrayList<>();

/**
* Creates a new {@code IntoLayerSpec} that will control the content of the given
* layer.
* @param intoLayer the layer
*/
public IntoLayerSpec(String intoLayer) {
this.intoLayer = intoLayer;
}

/**
* Adds patterns that control the content that is included in the layer. If no
* includes are specified then all content is included. If includes are specified
* then content must match an inclusion pattern and not match any exclusion
* patterns to be included.
* @param patterns the patterns to be included
*/
public void include(String... patterns) {
this.includes.addAll(Arrays.asList(patterns));
}

/**
* Adds patterns that control the content that is excluded from the layer. If no
* excludes a specified no content is excluded. If exclusions are specified then
* any content that matches an exclusion will be excluded irrespective of whether
* it matches an include pattern.
* @param patterns the patterns to be excluded
*/
public void exclude(String... patterns) {
this.includes.addAll(Arrays.asList(patterns));
}
Expand All @@ -201,8 +285,17 @@ <T> ContentSelector<T> asSelector(Function<String, ContentFilter<T>> filterFacto

}

/**
* An {@link IntoLayersSpec} that controls the layers to which application classes and
* resources belong.
*/
public static class ApplicationSpec extends IntoLayersSpec {

/**
* Creates a new {@code ApplicationSpec} with the given {@code contents}.
* @param contents specs for the layers in which application content should be
* included
*/
public ApplicationSpec(IntoLayerSpec... contents) {
super(contents);
}
Expand All @@ -213,8 +306,15 @@ List<ContentSelector<String>> asSelectors() {

}

/**
* An {@link IntoLayersSpec} that controls the layers to which dependencies belong.
*/
public static class DependenciesSpec extends IntoLayersSpec {

/**
* Creates a new {@code DependenciesSpec} with the given {@code contents}.
* @param contents specs for the layers in which dependencies should be included
*/
public DependenciesSpec(IntoLayerSpec... contents) {
super(contents);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
Expand Down Expand Up @@ -187,6 +188,36 @@ void bootJarLayered() throws IOException {
try (JarFile jar = new JarFile(file)) {
JarEntry entry = jar.getJarEntry("BOOT-INF/layers.idx");
assertThat(entry).isNotNull();
assertThat(Collections.list(jar.entries()).stream().map(JarEntry::getName)
.filter((name) -> name.startsWith("BOOT-INF/lib/spring-boot"))).isNotEmpty();
}
}

@TestTemplate
void bootJarLayeredCustom() throws IOException {
this.gradleBuild.script("src/docs/gradle/packaging/boot-jar-layered-custom").build("bootJar");
File file = new File(this.gradleBuild.getProjectDir(),
"build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar");
assertThat(file).isFile();
try (JarFile jar = new JarFile(file)) {
JarEntry entry = jar.getJarEntry("BOOT-INF/layers.idx");
assertThat(entry).isNotNull();
assertThat(Collections.list(jar.entries()).stream().map(JarEntry::getName)
.filter((name) -> name.startsWith("BOOT-INF/lib/spring-boot"))).isNotEmpty();
}
}

@TestTemplate
void bootJarLayeredExcludeTools() throws IOException {
this.gradleBuild.script("src/docs/gradle/packaging/boot-jar-layered-exclude-tools").build("bootJar");
File file = new File(this.gradleBuild.getProjectDir(),
"build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar");
assertThat(file).isFile();
try (JarFile jar = new JarFile(file)) {
JarEntry entry = jar.getJarEntry("BOOT-INF/layers.idx");
assertThat(entry).isNotNull();
assertThat(Collections.list(jar.entries()).stream().map(JarEntry::getName)
.filter((name) -> name.startsWith("BOOT-INF/lib/spring-boot"))).isEmpty();
}
}

Expand Down
Loading

0 comments on commit 34e6026

Please sign in to comment.