class ZipArchiveScatterOutputStream {

        private final ParallelScatterZipCreator creator;

        public ZipArchiveScatterOutputStream(ExecutorService executorService) {
            this.creator = new ParallelScatterZipCreator(executorService);
        }

        public void addEntry(ZipArchiveEntry entry, InputStreamSupplier supplier) {
            creator.addArchiveEntry(entry, supplier);
        }

        public void writeTo(ZipArchiveOutputStream archiveOutput) throws Exception {
            creator.writeTo(archiveOutput);
        }
    }

    @Override
    public void compressZip(String rootDir, String sourceDir, String outputZipFile,
                            Checksum checksum) throws Throwable {
        File rootFile = new File(Paths.get(rootDir, sourceDir).toString());
        File zipFile = new File(outputZipFile);
        LOG.info("Start to compress snapshot in parallel mode");
        FileUtils.forceMkdir(zipFile.getParentFile());

        ExecutorService compressExecutor =
                newFixedPool(compressThreads, compressThreads, "raft-snapshot-compress-executor",
                             new ThreadPoolExecutor.CallerRunsPolicy());
        ZipArchiveScatterOutputStream scatterOutput =
                new ZipArchiveScatterOutputStream(compressExecutor);
        compressDirectoryToZipFile(rootFile, scatterOutput, sourceDir, ZipEntry.DEFLATED);

        try (FileOutputStream fos = new FileOutputStream(zipFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos);
             CheckedOutputStream cos = new CheckedOutputStream(bos, checksum);
             ZipArchiveOutputStream archiveOutputStream = new ZipArchiveOutputStream(cos)) {
            scatterOutput.writeTo(archiveOutputStream);
            archiveOutputStream.flush();
            fos.getFD().sync();
        }

        ExecutorServiceHelper.shutdownAndAwaitTermination(compressExecutor);
    }

    @Override
    public void decompressZip(String sourceZipFile, String outputDir,
                              Checksum checksum) throws Throwable {
        LOG.info("Start to decompress snapshot in parallel mode");
        ExecutorService decompressExecutor =
                newFixedPool(decompressThreads, decompressThreads,
                             "raft-snapshot-decompress-executor",
                             new ThreadPoolExecutor.CallerRunsPolicy());
        // compute the checksum in a single thread
        Future<Boolean> checksumFuture = decompressExecutor.submit(() -> {
            computeZipFileChecksumValue(sourceZipFile, checksum);
            return true;
        });

        try (ZipFile zipFile = new ZipFile(sourceZipFile)) {
            List<Future<Boolean>> futures = Lists.newArrayList();
            for (Enumeration<ZipArchiveEntry> e = zipFile.getEntries();
                 e.hasMoreElements(); ) {
                ZipArchiveEntry zipEntry = e.nextElement();
                Future<Boolean> future = decompressExecutor.submit(() -> {
                    unZipFile(zipFile, zipEntry, outputDir);
                    return true;
                });
                futures.add(future);
            }
            // blocking and caching exception
            for (Future<Boolean> future : futures) {
                future.get();
            }
        }
        // wait for checksum to be calculated
        checksumFuture.get();
        ExecutorServiceHelper.shutdownAndAwaitTermination(decompressExecutor);
    }

    private void compressDirectoryToZipFile(File dir, ZipArchiveScatterOutputStream scatterOutput,
                                            String sourceDir, int method) {<FILL_FUNCTION_BODY>

        if (dir == null) {
            return;
        }
        if (dir.isFile()) {
            addEntry(sourceDir, dir, scatterOutput, method);
            return;
        }
        File[] files = Requires.requireNonNull(Objects.requireNonNull(dir.listFiles()), "files");
        for (File file : files) {
            String child = Paths.get(sourceDir, file.getName()).toString();
            if (file.isDirectory()) {
                compressDirectoryToZipFile(file, scatterOutput, child, method);
            } else {
                addEntry(child, file, scatterOutput, method);
            }
        }
    
<no_super_class>