Skip to content

Commit 34e6026

Browse files
committed
Polish new layered jar support
1 parent 3e936dd commit 34e6026

File tree

10 files changed

+182
-34
lines changed

10 files changed

+182
-34
lines changed

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-custom.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ bootJar {
2222
}
2323
intoLayer("dependencies")
2424
}
25-
layerOrder "dependencies", "spring-boot-loader", "snapshot-dependencies", "application"
25+
layerOrder = ["dependencies", "spring-boot-loader", "snapshot-dependencies", "application"]
2626
}
2727
}
2828
// end::layered[]

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-custom.gradle.kts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ plugins {
55
id("org.springframework.boot") version "{version}"
66
}
77

8+
tasks.getByName<BootJar>("bootJar") {
9+
mainClassName = "com.example.ExampleApplication"
10+
}
11+
812
// tag::layered[]
913
tasks.getByName<BootJar>("bootJar") {
1014
layered {
@@ -18,9 +22,9 @@ tasks.getByName<BootJar>("bootJar") {
1822
intoLayer("snapshot-dependencies") {
1923
include("*:*:*SNAPSHOT")
2024
}
21-
intoLayer("dependencies") {
25+
intoLayer("dependencies")
2226
}
23-
layersOrder("dependencies", "spring-boot-loader", "snapshot-dependencies", "application")
27+
layerOrder = listOf("dependencies", "spring-boot-loader", "snapshot-dependencies", "application")
2428
}
2529
}
2630
// end::layered[]

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-exclude-tools.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ bootJar {
99

1010
// tag::layered[]
1111
bootJar {
12-
layers {
12+
layered {
1313
includeLayerTools = false
1414
}
1515
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/gradle/packaging/boot-jar-layered-exclude-tools.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ tasks.getByName<BootJar>("bootJar") {
1111

1212
// tag::layered[]
1313
tasks.getByName<BootJar>("bootJar") {
14-
layers {
15-
includeLayerTools = false
14+
layered {
15+
isIncludeLayerTools = false
1616
}
1717
}
1818
// end::layered[]

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.Collections;
2121
import java.util.concurrent.Callable;
2222

23-
import groovy.lang.Closure;
2423
import org.gradle.api.Action;
2524
import org.gradle.api.artifacts.Configuration;
2625
import org.gradle.api.file.CopySpec;
@@ -33,7 +32,6 @@
3332
import org.gradle.api.tasks.Nested;
3433
import org.gradle.api.tasks.Optional;
3534
import org.gradle.api.tasks.bundling.Jar;
36-
import org.gradle.util.ConfigureUtil;
3735

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

159+
/**
160+
* Returns the spec that describes the layers in a layerd jar.
161+
* @return the spec for the layers or {@code null}.
162+
* @since 2.3.0
163+
*/
161164
@Nested
162165
@Optional
163166
public LayeredSpec getLayered() {
164167
return this.layered;
165168
}
166169

170+
/**
171+
* Configures the jar to be layered using the default layering.
172+
* @since 2.3.0
173+
*/
167174
public void layered() {
168-
layered(true);
169-
}
170-
171-
public void layered(boolean layered) {
172-
this.layered = layered ? new LayeredSpec() : null;
173-
}
174-
175-
public void layered(Closure<?> closure) {
176-
layered(ConfigureUtil.configureUsing(closure));
175+
enableLayeringIfNecessary();
177176
}
178177

178+
/**
179+
* Configures the jar to be layered, customizing the layers using the given
180+
* {@code action}.
181+
* @param action the action to apply
182+
* @since 2.3.0
183+
*/
179184
public void layered(Action<LayeredSpec> action) {
180-
LayeredSpec layered = new LayeredSpec();
181-
action.execute(layered);
182-
this.layered = layered;
185+
action.execute(enableLayeringIfNecessary());
183186
}
184187

185188
@Override
@@ -258,6 +261,7 @@ protected ZipCompression resolveZipCompression(FileCopyDetails details) {
258261
* {@code BOOT-INF/lib} is considered to be a library.
259262
* @param details the file copy details
260263
* @return {@code true} if the details are for a library
264+
* @since 2.3.0
261265
*/
262266
protected boolean isLibrary(FileCopyDetails details) {
263267
String path = details.getRelativePath().getPathString();
@@ -273,6 +277,13 @@ private LaunchScriptConfiguration enableLaunchScriptIfNecessary() {
273277
return launchScript;
274278
}
275279

280+
private LayeredSpec enableLayeringIfNecessary() {
281+
if (this.layered == null) {
282+
this.layered = new LayeredSpec();
283+
}
284+
return this.layered;
285+
}
286+
276287
/**
277288
* Syntactic sugar that makes {@link CopySpec#into} calls a little easier to read.
278289
* @param <T> the result type

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayeredSpec.java

Lines changed: 112 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,59 +61,117 @@ public class LayeredSpec {
6161

6262
private Layers layers;
6363

64+
/**
65+
* Returns whether the layer tools should be included as a dependency in the layered
66+
* jar.
67+
* @return whether the layer tools should be included
68+
*/
6469
@Input
6570
public boolean isIncludeLayerTools() {
6671
return this.includeLayerTools;
6772
}
6873

74+
/**
75+
* Sets whether the layer tools should be included as a dependency in the layered jar.
76+
* @param includeLayerTools {@code true} if the layer tools should be included,
77+
* otherwise {@code false}
78+
*/
6979
public void setIncludeLayerTools(boolean includeLayerTools) {
7080
this.includeLayerTools = includeLayerTools;
7181
}
7282

83+
/**
84+
* Returns the {@link ApplicationSpec} that controls the layers to which application
85+
* classes and resources belong.
86+
* @return the application spec
87+
*/
7388
@Input
7489
public ApplicationSpec getApplication() {
7590
return this.application;
7691
}
7792

78-
public void application(ApplicationSpec spec) {
93+
/**
94+
* Sets the {@link ApplicationSpec} that controls the layers to which application
95+
* classes are resources belong.
96+
* @param spec the application spec
97+
*/
98+
public void setApplication(ApplicationSpec spec) {
7999
this.application = spec;
80100
}
81101

82-
public void application(Closure<?> closure) {
83-
application(ConfigureUtil.configureUsing(closure));
84-
}
85-
102+
/**
103+
* Customizes the {@link ApplicationSpec} using the given {@code action}.
104+
* @param action the action
105+
*/
86106
public void application(Action<ApplicationSpec> action) {
87107
action.execute(this.application);
88108
}
89109

110+
/**
111+
* Customizes the {@link ApplicationSpec} using the given {@code closure}.
112+
* @param closure the closure
113+
*/
114+
public void application(Closure<?> closure) {
115+
application(ConfigureUtil.configureUsing(closure));
116+
}
117+
118+
/**
119+
* Returns the {@link DependenciesSpec} that controls the layers to which dependencies
120+
* belong.
121+
* @return the dependencies spec
122+
*/
90123
@Input
91124
public DependenciesSpec getDependencies() {
92125
return this.dependencies;
93126
}
94127

95-
public void dependencies(DependenciesSpec spec) {
128+
/**
129+
* Sets the {@link DependenciesSpec} that controls the layers to which dependencies
130+
* belong.
131+
* @param spec the dependencies spec
132+
*/
133+
public void setDependencies(DependenciesSpec spec) {
96134
this.dependencies = spec;
97135
}
98136

99-
public void dependencies(Closure<?> closure) {
100-
dependencies(ConfigureUtil.configureUsing(closure));
101-
}
102-
137+
/**
138+
* Customizes the {@link DependenciesSpec} using the given {@code action}.
139+
* @param action the action
140+
*/
103141
public void dependencies(Action<DependenciesSpec> action) {
104142
action.execute(this.dependencies);
105143
}
106144

145+
/**
146+
* Customizes the {@link DependenciesSpec} using the given {@code closure}.
147+
* @param closure the closure
148+
*/
149+
public void dependencies(Closure<?> closure) {
150+
dependencies(ConfigureUtil.configureUsing(closure));
151+
}
152+
153+
/**
154+
* Returns the order of the layers in the jar from least to most frequently changing.
155+
* @return the layer order
156+
*/
107157
@Input
108158
public List<String> getLayerOrder() {
109159
return this.layerOrder;
110160
}
111161

112-
public void layerOrder(String... layerOrder) {
162+
/**
163+
* Sets to order of the layers in the jar from least to most frequently changing.
164+
* @param layerOrder the layer order
165+
*/
166+
public void setLayerOrder(String... layerOrder) {
113167
this.layerOrder = Arrays.asList(layerOrder);
114168
}
115169

116-
public void layerOrder(List<String> layerOrder) {
170+
/**
171+
* Sets to order of the layers in the jar from least to most frequently changing.
172+
* @param layerOrder the layer order
173+
*/
174+
public void setLayerOrder(List<String> layerOrder) {
117175
this.layerOrder = layerOrder;
118176
}
119177

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

202+
/**
203+
* Base class for specs that control the layers to which a category of content should
204+
* belong.
205+
*/
144206
public abstract static class IntoLayersSpec implements Serializable {
145207

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

175237
}
176238

239+
/**
240+
* Spec that controls the content that should be part of a particular layer.
241+
*/
177242
public static class IntoLayerSpec implements Serializable {
178243

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

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

250+
/**
251+
* Creates a new {@code IntoLayerSpec} that will control the content of the given
252+
* layer.
253+
* @param intoLayer the layer
254+
*/
185255
public IntoLayerSpec(String intoLayer) {
186256
this.intoLayer = intoLayer;
187257
}
188258

259+
/**
260+
* Adds patterns that control the content that is included in the layer. If no
261+
* includes are specified then all content is included. If includes are specified
262+
* then content must match an inclusion pattern and not match any exclusion
263+
* patterns to be included.
264+
* @param patterns the patterns to be included
265+
*/
189266
public void include(String... patterns) {
190267
this.includes.addAll(Arrays.asList(patterns));
191268
}
192269

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

202286
}
203287

288+
/**
289+
* An {@link IntoLayersSpec} that controls the layers to which application classes and
290+
* resources belong.
291+
*/
204292
public static class ApplicationSpec extends IntoLayersSpec {
205293

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

214307
}
215308

309+
/**
310+
* An {@link IntoLayersSpec} that controls the layers to which dependencies belong.
311+
*/
216312
public static class DependenciesSpec extends IntoLayersSpec {
217313

314+
/**
315+
* Creates a new {@code DependenciesSpec} with the given {@code contents}.
316+
* @param contents specs for the layers in which dependencies should be included
317+
*/
218318
public DependenciesSpec(IntoLayerSpec... contents) {
219319
super(contents);
220320
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.FileReader;
2222
import java.io.FileWriter;
2323
import java.io.IOException;
24+
import java.util.Collections;
2425
import java.util.jar.JarEntry;
2526
import java.util.jar.JarFile;
2627
import java.util.jar.JarOutputStream;
@@ -187,6 +188,36 @@ void bootJarLayered() throws IOException {
187188
try (JarFile jar = new JarFile(file)) {
188189
JarEntry entry = jar.getJarEntry("BOOT-INF/layers.idx");
189190
assertThat(entry).isNotNull();
191+
assertThat(Collections.list(jar.entries()).stream().map(JarEntry::getName)
192+
.filter((name) -> name.startsWith("BOOT-INF/lib/spring-boot"))).isNotEmpty();
193+
}
194+
}
195+
196+
@TestTemplate
197+
void bootJarLayeredCustom() throws IOException {
198+
this.gradleBuild.script("src/docs/gradle/packaging/boot-jar-layered-custom").build("bootJar");
199+
File file = new File(this.gradleBuild.getProjectDir(),
200+
"build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar");
201+
assertThat(file).isFile();
202+
try (JarFile jar = new JarFile(file)) {
203+
JarEntry entry = jar.getJarEntry("BOOT-INF/layers.idx");
204+
assertThat(entry).isNotNull();
205+
assertThat(Collections.list(jar.entries()).stream().map(JarEntry::getName)
206+
.filter((name) -> name.startsWith("BOOT-INF/lib/spring-boot"))).isNotEmpty();
207+
}
208+
}
209+
210+
@TestTemplate
211+
void bootJarLayeredExcludeTools() throws IOException {
212+
this.gradleBuild.script("src/docs/gradle/packaging/boot-jar-layered-exclude-tools").build("bootJar");
213+
File file = new File(this.gradleBuild.getProjectDir(),
214+
"build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar");
215+
assertThat(file).isFile();
216+
try (JarFile jar = new JarFile(file)) {
217+
JarEntry entry = jar.getJarEntry("BOOT-INF/layers.idx");
218+
assertThat(entry).isNotNull();
219+
assertThat(Collections.list(jar.entries()).stream().map(JarEntry::getName)
220+
.filter((name) -> name.startsWith("BOOT-INF/lib/spring-boot"))).isEmpty();
190221
}
191222
}
192223

0 commit comments

Comments
 (0)