Search code examples
javaclassloader

How do I get the list of files in a directory in a classpath that is included from a JAR?


How do I get the list of files in a directory in a classpath that is included from a JAR?

The question is not about opening a JAR file literally and finding files under a directory in it. The question is specifically how to list files in a directory that happened to be in a classpath because the JAR was included in the classpath. So there should be no opening JAR files involved. If this isn't possible please explain why and how it should be done without knowing the filename of the jar beforehand.

Say the project is dependent on another project whose resource structure is as the following:

src/main/resources/testFolder
 - fileA.txt
 - fileB.txt

Given that the testFolder being available in the classpath, how do I enumerate the files under it?

The testFolder ends up being inside the JAR which is inside the WAR's lib folder as where dependencies should be.


Solution

  •     PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
        Resource[] resources;
        try {
            resources = scanner.getResources("classpath*:testFolder/**/*.*");
            for (int i = 0; i < resources.length; i++) {
                log.info("resource: {}", resources[i].getFilename() );
            }
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    

    Upon reading the underlying implementation, I found the following:

    URLConnection con = rootDirResource.getURL().openConnection();
    JarFile jarFile;
    String jarFileUrl;
    String rootEntryPath;
    boolean newJarFile = false;
    
    if (con instanceof JarURLConnection) {
        // Should usually be the case for traditional JAR files.
        JarURLConnection jarCon = (JarURLConnection) con;
        ResourceUtils.useCachesIfNecessary(jarCon);
        jarFile = jarCon.getJarFile();
        jarFileUrl = jarCon.getJarFileURL().toExternalForm();
        JarEntry jarEntry = jarCon.getJarEntry();
        rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
    }
    else {
        // No JarURLConnection -> need to resort to URL file parsing.
        // We'll assume URLs of the format "jar:path!/entry", with the protocol
        // being arbitrary as long as following the entry format.
        // We'll also handle paths with and without leading "file:" prefix.
        String urlFile = rootDirResource.getURL().getFile();
        int separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR);
        if (separatorIndex != -1) {
            jarFileUrl = urlFile.substring(0, separatorIndex);
            rootEntryPath = urlFile.substring(separatorIndex + ResourceUtils.JAR_URL_SEPARATOR.length());
            jarFile = getJarFile(jarFileUrl);
        }
        else {
            jarFile = new JarFile(urlFile);
            jarFileUrl = urlFile;
            rootEntryPath = "";
        }
        newJarFile = true;
    }
    

    Looking at Spring's implementation, it seems the only way to do it is to actually treat the resource as a JAR file.