48
48
import org .springframework .boot .loader .tools .DefaultLaunchScript ;
49
49
import org .springframework .boot .loader .tools .FileUtils ;
50
50
import org .springframework .boot .loader .tools .JarModeLibrary ;
51
+ import org .springframework .boot .loader .tools .Layer ;
52
+ import org .springframework .boot .loader .tools .LayersIndex ;
53
+ import org .springframework .util .Assert ;
54
+ import org .springframework .util .FileCopyUtils ;
51
55
import org .springframework .util .StreamUtils ;
56
+ import org .springframework .util .StringUtils ;
52
57
53
58
/**
54
59
* A {@link CopyAction} for creating a Spring Boot zip archive (typically a jar or war).
@@ -70,7 +75,7 @@ class BootZipCopyAction implements CopyAction {
70
75
71
76
private final boolean includeDefaultLoader ;
72
77
73
- private final boolean includeLayerTools ;
78
+ private final String layerToolsLocation ;
74
79
75
80
private final Spec <FileTreeElement > requiresUnpack ;
76
81
@@ -87,15 +92,15 @@ class BootZipCopyAction implements CopyAction {
87
92
private final LayerResolver layerResolver ;
88
93
89
94
BootZipCopyAction (File output , Manifest manifest , boolean preserveFileTimestamps , boolean includeDefaultLoader ,
90
- boolean includeLayerTools , Spec <FileTreeElement > requiresUnpack , Spec <FileTreeElement > exclusions ,
95
+ String layerToolsLocation , Spec <FileTreeElement > requiresUnpack , Spec <FileTreeElement > exclusions ,
91
96
LaunchScriptConfiguration launchScript , Spec <FileCopyDetails > librarySpec ,
92
97
Function <FileCopyDetails , ZipCompression > compressionResolver , String encoding ,
93
98
LayerResolver layerResolver ) {
94
99
this .output = output ;
95
100
this .manifest = manifest ;
96
101
this .preserveFileTimestamps = preserveFileTimestamps ;
97
102
this .includeDefaultLoader = includeDefaultLoader ;
98
- this .includeLayerTools = includeLayerTools ;
103
+ this .layerToolsLocation = layerToolsLocation ;
99
104
this .requiresUnpack = requiresUnpack ;
100
105
this .exclusions = exclusions ;
101
106
this .launchScript = launchScript ;
@@ -177,14 +182,18 @@ private class Processor {
177
182
178
183
private ZipArchiveOutputStream out ;
179
184
180
- private Spec <FileTreeElement > writtenLoaderEntries ;
185
+ private final LayersIndex layerIndex ;
186
+
187
+ private LoaderZipEntries .WrittenEntries writtenLoaderEntries ;
181
188
182
189
private Set <String > writtenDirectories = new LinkedHashSet <>();
183
190
184
191
private Set <String > writtenLibraries = new LinkedHashSet <>();
185
192
186
193
Processor (ZipArchiveOutputStream out ) {
187
194
this .out = out ;
195
+ this .layerIndex = (BootZipCopyAction .this .layerResolver != null )
196
+ ? new LayersIndex (BootZipCopyAction .this .layerResolver .getLayers ()) : null ;
188
197
}
189
198
190
199
void process (FileCopyDetails details ) {
@@ -207,11 +216,11 @@ void process(FileCopyDetails details) {
207
216
208
217
private boolean skipProcessing (FileCopyDetails details ) {
209
218
return BootZipCopyAction .this .exclusions .isSatisfiedBy (details )
210
- || (this .writtenLoaderEntries != null && this .writtenLoaderEntries .isSatisfiedBy (details ));
219
+ || (this .writtenLoaderEntries != null && this .writtenLoaderEntries .isWrittenDirectory (details ));
211
220
}
212
221
213
222
private void processDirectory (FileCopyDetails details ) throws IOException {
214
- String name = getEntryName ( details );
223
+ String name = details . getRelativePath (). getPathString ( );
215
224
long time = getTime (details );
216
225
writeParentDirectoriesIfNecessary (name , time );
217
226
ZipArchiveEntry entry = new ZipArchiveEntry (name + '/' );
@@ -223,7 +232,7 @@ private void processDirectory(FileCopyDetails details) throws IOException {
223
232
}
224
233
225
234
private void processFile (FileCopyDetails details ) throws IOException {
226
- String name = getEntryName ( details );
235
+ String name = details . getRelativePath (). getPathString ( );
227
236
long time = getTime (details );
228
237
writeParentDirectoriesIfNecessary (name , time );
229
238
ZipArchiveEntry entry = new ZipArchiveEntry (name );
@@ -239,6 +248,10 @@ private void processFile(FileCopyDetails details) throws IOException {
239
248
if (BootZipCopyAction .this .librarySpec .isSatisfiedBy (details )) {
240
249
this .writtenLibraries .add (name .substring (name .lastIndexOf ('/' ) + 1 ));
241
250
}
251
+ if (BootZipCopyAction .this .layerResolver != null ) {
252
+ Layer layer = BootZipCopyAction .this .layerResolver .getLayer (details );
253
+ this .layerIndex .add (layer , name );
254
+ }
242
255
}
243
256
244
257
private void writeParentDirectoriesIfNecessary (String name , long time ) throws IOException {
@@ -261,34 +274,12 @@ private String getParentDirectory(String name) {
261
274
return name .substring (0 , lastSlash );
262
275
}
263
276
264
- private String getEntryName (FileCopyDetails details ) {
265
- if (BootZipCopyAction .this .layerResolver == null ) {
266
- return details .getRelativePath ().getPathString ();
267
- }
268
- return BootZipCopyAction .this .layerResolver .getPath (details );
269
- }
270
-
271
- private void prepareStoredEntry (FileCopyDetails details , ZipArchiveEntry archiveEntry ) throws IOException {
272
- archiveEntry .setMethod (java .util .zip .ZipEntry .STORED );
273
- archiveEntry .setSize (details .getSize ());
274
- archiveEntry .setCompressedSize (details .getSize ());
275
- archiveEntry .setCrc (getCrc (details ));
276
- if (BootZipCopyAction .this .requiresUnpack .isSatisfiedBy (details )) {
277
- archiveEntry .setComment ("UNPACK:" + FileUtils .sha1Hash (details .getFile ()));
278
- }
279
- }
280
-
281
- private long getCrc (FileCopyDetails details ) {
282
- Crc32OutputStream crcStream = new Crc32OutputStream ();
283
- details .copyTo (crcStream );
284
- return crcStream .getCrc ();
285
- }
286
-
287
277
void finish () throws IOException {
288
278
writeLoaderEntriesIfNecessary (null );
289
279
writeJarToolsIfNecessary ();
290
- writeLayersIndexIfNecessary ();
291
280
writeClassPathIndexIfNecessary ();
281
+ // We must write the layer index last
282
+ writeLayersIndexIfNecessary ();
292
283
}
293
284
294
285
private void writeLoaderEntriesIfNecessary (FileCopyDetails details ) throws IOException {
@@ -299,9 +290,15 @@ private void writeLoaderEntriesIfNecessary(FileCopyDetails details) throws IOExc
299
290
// Don't write loader entries until after META-INF folder (see gh-16698)
300
291
return ;
301
292
}
302
- LoaderZipEntries entries = new LoaderZipEntries (
293
+ LoaderZipEntries loaderEntries = new LoaderZipEntries (
303
294
BootZipCopyAction .this .preserveFileTimestamps ? null : CONSTANT_TIME_FOR_ZIP_ENTRIES );
304
- this .writtenLoaderEntries = entries .writeTo (this .out );
295
+ this .writtenLoaderEntries = loaderEntries .writeTo (this .out );
296
+ if (BootZipCopyAction .this .layerResolver != null ) {
297
+ for (String name : this .writtenLoaderEntries .getFiles ()) {
298
+ Layer layer = BootZipCopyAction .this .layerResolver .getLayer (name );
299
+ this .layerIndex .add (layer , name );
300
+ }
301
+ }
305
302
}
306
303
307
304
private boolean isInMetaInf (FileCopyDetails details ) {
@@ -313,44 +310,75 @@ private boolean isInMetaInf(FileCopyDetails details) {
313
310
}
314
311
315
312
private void writeJarToolsIfNecessary () throws IOException {
316
- if (BootZipCopyAction .this .layerResolver == null || ! BootZipCopyAction . this . includeLayerTools ) {
317
- return ;
313
+ if (BootZipCopyAction .this .layerToolsLocation != null ) {
314
+ writeJarModeLibrary ( BootZipCopyAction . this . layerToolsLocation , JarModeLibrary . LAYER_TOOLS ) ;
318
315
}
319
- writeJarModeLibrary (JarModeLibrary .LAYER_TOOLS );
320
316
}
321
317
322
- private void writeJarModeLibrary (JarModeLibrary jarModeLibrary ) throws IOException {
323
- String name = BootZipCopyAction .this .layerResolver .getPath (jarModeLibrary );
324
- writeFile (name , ZipEntryWriter .fromInputStream (jarModeLibrary .openStream ()));
318
+ private void writeJarModeLibrary (String location , JarModeLibrary library ) throws IOException {
319
+ String name = location + library .getName ();
320
+ writeEntry (name , ZipEntryWriter .fromInputStream (library .openStream ()), false ,
321
+ (entry ) -> prepareStoredEntry (library .openStream (), entry ));
322
+ if (BootZipCopyAction .this .layerResolver != null ) {
323
+ Layer layer = BootZipCopyAction .this .layerResolver .getLayer (library );
324
+ this .layerIndex .add (layer , name );
325
+ }
325
326
}
326
327
327
- private void writeLayersIndexIfNecessary () throws IOException {
328
- Attributes manifestAttributes = BootZipCopyAction .this .manifest .getAttributes ();
329
- String layersIndex = (String ) manifestAttributes .get ("Spring-Boot-Layers-Index" );
330
- if (layersIndex != null && BootZipCopyAction .this .layerResolver != null ) {
331
- writeFile (layersIndex , ZipEntryWriter .fromLines (BootZipCopyAction .this .encoding ,
332
- BootZipCopyAction .this .layerResolver .getLayerNames ()));
328
+ private void prepareStoredEntry (FileCopyDetails details , ZipArchiveEntry archiveEntry ) throws IOException {
329
+ prepareStoredEntry (details .open (), archiveEntry );
330
+ if (BootZipCopyAction .this .requiresUnpack .isSatisfiedBy (details )) {
331
+ archiveEntry .setComment ("UNPACK:" + FileUtils .sha1Hash (details .getFile ()));
333
332
}
334
333
}
335
334
335
+ private void prepareStoredEntry (InputStream input , ZipArchiveEntry archiveEntry ) throws IOException {
336
+ archiveEntry .setMethod (java .util .zip .ZipEntry .STORED );
337
+ Crc32OutputStream crcStream = new Crc32OutputStream ();
338
+ int size = FileCopyUtils .copy (input , crcStream );
339
+ archiveEntry .setSize (size );
340
+ archiveEntry .setCompressedSize (size );
341
+ archiveEntry .setCrc (crcStream .getCrc ());
342
+ }
343
+
336
344
private void writeClassPathIndexIfNecessary () throws IOException {
337
345
Attributes manifestAttributes = BootZipCopyAction .this .manifest .getAttributes ();
338
346
String classPathIndex = (String ) manifestAttributes .get ("Spring-Boot-Classpath-Index" );
339
347
if (classPathIndex != null ) {
340
- writeFile (classPathIndex ,
341
- ZipEntryWriter .fromLines (BootZipCopyAction .this .encoding , this .writtenLibraries ));
348
+ writeEntry (classPathIndex ,
349
+ ZipEntryWriter .fromLines (BootZipCopyAction .this .encoding , this .writtenLibraries ), true );
350
+ }
351
+ }
352
+
353
+ private void writeLayersIndexIfNecessary () throws IOException {
354
+ if (BootZipCopyAction .this .layerResolver != null ) {
355
+ Attributes manifestAttributes = BootZipCopyAction .this .manifest .getAttributes ();
356
+ String name = (String ) manifestAttributes .get ("Spring-Boot-Layers-Index" );
357
+ Assert .state (StringUtils .hasText (name ), "Missing layer index manifest attribute" );
358
+ Layer layer = BootZipCopyAction .this .layerResolver .getLayer (name );
359
+ this .layerIndex .add (layer , name );
360
+ writeEntry (name , (entry , out ) -> this .layerIndex .writeTo (out ), false );
342
361
}
343
362
}
344
363
345
- private void writeFile (String name , ZipEntryWriter entryWriter ) throws IOException {
364
+ private void writeEntry (String name , ZipEntryWriter entryWriter , boolean addToLayerIndex ) throws IOException {
365
+ writeEntry (name , entryWriter , addToLayerIndex , ZipEntryCustomizer .NONE );
366
+ }
367
+
368
+ private void writeEntry (String name , ZipEntryWriter entryWriter , boolean addToLayerIndex ,
369
+ ZipEntryCustomizer entryCustomizer ) throws IOException {
346
370
writeParentDirectoriesIfNecessary (name , CONSTANT_TIME_FOR_ZIP_ENTRIES );
347
371
ZipArchiveEntry entry = new ZipArchiveEntry (name );
348
372
entry .setUnixMode (UnixStat .FILE_FLAG );
349
373
entry .setTime (CONSTANT_TIME_FOR_ZIP_ENTRIES );
374
+ entryCustomizer .customize (entry );
350
375
this .out .putArchiveEntry (entry );
351
376
entryWriter .writeTo (entry , this .out );
352
377
this .out .closeArchiveEntry ();
353
-
378
+ if (addToLayerIndex && BootZipCopyAction .this .layerResolver != null ) {
379
+ Layer layer = BootZipCopyAction .this .layerResolver .getLayer (name );
380
+ this .layerIndex .add (layer , name );
381
+ }
354
382
}
355
383
356
384
private long getTime (FileCopyDetails details ) {
@@ -360,6 +388,24 @@ private long getTime(FileCopyDetails details) {
360
388
361
389
}
362
390
391
+ /**
392
+ * Callback interface used to customize a {@link ZipArchiveEntry}.
393
+ */
394
+ @ FunctionalInterface
395
+ private interface ZipEntryCustomizer {
396
+
397
+ ZipEntryCustomizer NONE = (entry ) -> {
398
+ };
399
+
400
+ /**
401
+ * Customize the entry.
402
+ * @param entry the entry to customize
403
+ * @throws IOException on IO error
404
+ */
405
+ void customize (ZipArchiveEntry entry ) throws IOException ;
406
+
407
+ }
408
+
363
409
/**
364
410
* Callback used to write a zip entry data.
365
411
*/
0 commit comments