From 61eccdb0a4a3bc9422c1bc6e172cb34c6612b21f Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Mon, 8 Jun 2026 14:50:33 -0400 Subject: [PATCH 1/3] Fix ZIP CRC regression by avoiding CachingOutputStream for temporary fragments CachingOutputStream is inappropriate for temporary scatter fragments as it adds unnecessary complexity and potential for data corruption under high load on Linux. Switching to a plain BufferedOutputStream. --- .../codehaus/plexus/archiver/zip/OffloadingOutputStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java b/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java index a71d3053..9929a385 100644 --- a/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java +++ b/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java @@ -113,7 +113,7 @@ protected OutputStream getStream() throws IOException { @Override protected void thresholdReached() throws IOException { outputPath = Files.createTempFile(prefix, suffix); - currentOutputStream = Streams.fileOutputStream(outputPath); + currentOutputStream = Streams.bufferedOutputStream(Files.newOutputStream(outputPath)); } public InputStream getInputStream() throws IOException { From d79499c4a624eb97929580db262d618e1d334b99 Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Mon, 8 Jun 2026 15:10:20 -0400 Subject: [PATCH 2/3] Improve thread management in ConcurrentJarCreator Explicitly manage and shut down the ExecutorService in ConcurrentJarCreator to avoid resource leaks after parallel archiving is complete. --- .../archiver/zip/ConcurrentJarCreator.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/archiver/zip/ConcurrentJarCreator.java b/src/main/java/org/codehaus/plexus/archiver/zip/ConcurrentJarCreator.java index 4b2e7d74..4b39c361 100644 --- a/src/main/java/org/codehaus/plexus/archiver/zip/ConcurrentJarCreator.java +++ b/src/main/java/org/codehaus/plexus/archiver/zip/ConcurrentJarCreator.java @@ -23,6 +23,7 @@ import java.io.SequenceInputStream; import java.io.UncheckedIOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.Deflater; import java.util.zip.ZipEntry; @@ -55,6 +56,8 @@ public class ConcurrentJarCreator { private final ParallelScatterZipCreator parallelScatterZipCreator; + private final ExecutorService es; + private long zipCloseElapsed; private static class DeferredSupplier implements ScatterGatherBackingStoreSupplier { @@ -118,8 +121,8 @@ public ConcurrentJarCreator(boolean compressAddedZips, int nThreads) throws IOEx manifest = createDeferred(defaultSupplier); directories = createDeferred(defaultSupplier); synchronousEntries = createDeferred(defaultSupplier); - parallelScatterZipCreator = - new ParallelScatterZipCreator(Executors.newFixedThreadPool(nThreads), defaultSupplier); + es = Executors.newFixedThreadPool(nThreads); + parallelScatterZipCreator = new ParallelScatterZipCreator(es, defaultSupplier); } /** @@ -161,11 +164,15 @@ public void addArchiveEntry( public void writeTo(ZipArchiveOutputStream targetStream) throws IOException, ExecutionException, InterruptedException { - metaInfDir.writeTo(targetStream); - manifest.writeTo(targetStream); - directories.writeTo(targetStream); - synchronousEntries.writeTo(targetStream); - parallelScatterZipCreator.writeTo(targetStream); + try { + metaInfDir.writeTo(targetStream); + manifest.writeTo(targetStream); + directories.writeTo(targetStream); + synchronousEntries.writeTo(targetStream); + parallelScatterZipCreator.writeTo(targetStream); + } finally { + es.shutdown(); + } long startAt = System.currentTimeMillis(); targetStream.close(); zipCloseElapsed = System.currentTimeMillis() - startAt; From 8873b0a09f1e227d27882e7c8f4a4d902be058a5 Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Mon, 8 Jun 2026 15:12:21 -0400 Subject: [PATCH 3/3] Optimize buffer reuse and improve NIO usage in ZIP archiver - Refine ByteArrayOutputStream.reset() to safely handle empty buffers. - Use Path and Files API in DeferredScatterOutputStream and OffloadingOutputStream for better performance and reliability. - Expose Path in OffloadingOutputStream to avoid unnecessary File object conversions. --- .../plexus/archiver/zip/ByteArrayOutputStream.java | 2 +- .../plexus/archiver/zip/DeferredScatterOutputStream.java | 7 ++++--- .../plexus/archiver/zip/OffloadingOutputStream.java | 4 ++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/archiver/zip/ByteArrayOutputStream.java b/src/main/java/org/codehaus/plexus/archiver/zip/ByteArrayOutputStream.java index 7a6bc952..2b767534 100644 --- a/src/main/java/org/codehaus/plexus/archiver/zip/ByteArrayOutputStream.java +++ b/src/main/java/org/codehaus/plexus/archiver/zip/ByteArrayOutputStream.java @@ -262,7 +262,7 @@ public synchronized void reset() { } else { // Throw away old buffers currentBuffer = null; - int size = buffers.get(0).length; + int size = buffers.isEmpty() ? 1024 : buffers.get(0).length; buffers.clear(); needNewBuffer(size); reuseBuffers = true; diff --git a/src/main/java/org/codehaus/plexus/archiver/zip/DeferredScatterOutputStream.java b/src/main/java/org/codehaus/plexus/archiver/zip/DeferredScatterOutputStream.java index dda5eb2f..21d8758f 100644 --- a/src/main/java/org/codehaus/plexus/archiver/zip/DeferredScatterOutputStream.java +++ b/src/main/java/org/codehaus/plexus/archiver/zip/DeferredScatterOutputStream.java @@ -17,9 +17,10 @@ */ package org.codehaus.plexus.archiver.zip; -import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import org.apache.commons.compress.parallel.ScatterGatherBackingStore; @@ -48,9 +49,9 @@ public void closeForWriting() throws IOException { @Override public void close() throws IOException { - File file = dfos.getFile(); + Path file = dfos.getOutputPath(); if (file != null) { - file.delete(); + Files.deleteIfExists(file); } } } diff --git a/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java b/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java index 9929a385..13f3a4c0 100644 --- a/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java +++ b/src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java @@ -142,6 +142,10 @@ public byte[] getData() { return null; } + public Path getOutputPath() { + return outputPath; + } + /** * Returns either the output file specified in the constructor or * the temporary file created or null.