'get FileSystems of a jar inside another jar

This is what I'm trying to do:

FileSystem fs1 = FileSystems.newFileSystem(Paths.get("f1.jar"), null);
FileSystem fs2 = FileSystems.newFileSystem(fs1.getPath("/f2.jar"), null);

but I get a java.nio.file.ProviderNotFoundException thrown by FileSystems.newFileSystem() on the second line.

What am I doing wrong?

Thanks!



Solution 1:[1]

Given a jarfile URI like: jar:file:/console/console.jar!/BOOT-INF/lib/generator.jar!/generator/static/

I wrote the following method to extract the jars recursively and return all of the file systems:

private FileSystem getNestedFileSystems(URI generatorAssets) throws IOException {
    String[] jarFiles = generatorAssets.toString().split("!");
    URI rootJarUri = URI.create(jarFiles[0]);
    FileSystem currentFs;
    try {
        currentFs = FileSystems.getFileSystem(rootJarUri);
    } catch (FileSystemNotFoundException fsnf) {
        currentFs = FileSystems.newFileSystem(rootJarUri, Map.of("create", "true"));
    }
    Path currentJar = null;
    if (jarFiles.length > 2) {
        for (int i = 1; i < (jarFiles.length - 1); i++) {
            Path nestedJar = currentFs.getPath(jarFiles[i]);
            Path extractedJar = Files.createTempFile("jar-" + i, ".jar");
            Files.copy(nestedJar, extractedJar, StandardCopyOption.REPLACE_EXISTING);
            currentFs.close();
            if (currentJar != null) {
                Files.delete(currentJar);
            }
            currentJar = extractedJar;
            currentFs = FileSystems.newFileSystem(URI.create("jar:file:" + extractedJar),
                    Map.of("create", "true"));
        }
    }
    return currentFs;
}

Returning a list of filesystems so you can close them all after use and not leak FD's. The last FileSystem will be able to get your desired directory/object/etc

Solution 2:[2]

The problem is solved at least in Java 14 (maybe earlier). OP's code works there with one change: you need to add an explicit cast to ClassLoader for the second argument of newFileSystem.

For a more complete example, this code prints the contents of README.md which is inside the internal.zip archive which itself is inside the archive.zip archive:

import java.io.IOException;
import java.nio.file.*;

public class Test {
    public static void main(String[] args) throws IOException {
        Path zipfile = Paths.get("archive.zip");
        FileSystem manager = FileSystems.newFileSystem(zipfile, (ClassLoader) null);
        Path internal = manager.getPath("internal.zip");
        FileSystem internalManager = FileSystems.newFileSystem(internal, (ClassLoader) null);
        Path internalFile = internalManager.getPath("README.md");
        Files.lines(internalFile).forEach(System.out::println);
    }
}

Note that file names inside archives are case-sensitive.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 vadipp