How to use ClassLoader.getResources() correctly?

214,421

Solution 1

There is no way to recursively search through the classpath. You need to know the Full pathname of a resource to be able to retrieve it in this way. The resource may be in a directory in the file system or in a jar file so it is not as simple as performing a directory listing of "the classpath". You will need to provide the full path of the resource e.g. '/com/mypath/bla.xml'.

For your second question, getResource will return the first resource that matches the given resource name. The order that the class path is searched is given in the javadoc for getResource.

Solution 2

The Spring Framework has a class which allows to recursively search through the classpath:

PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
resolver.getResources("classpath*:some/package/name/**/*.xml");

Solution 3

This is the simplest wat to get the File object to which a certain URL object is pointing at:

File file=new File(url.toURI());

Now, for your concrete questions:

  • finding all resources in the META-INF "directory":

You can indeed get the File object pointing to this URL

Enumeration<URL> en=getClass().getClassLoader().getResources("META-INF");
if (en.hasMoreElements()) {
    URL metaInf=en.nextElement();
    File fileMetaInf=new File(metaInf.toURI());

    File[] files=fileMetaInf.listFiles();
    //or 
    String[] filenames=fileMetaInf.list();
}
  • all resources named bla.xml (recursivly)

In this case, you'll have to do some custom code. Here is a dummy example:

final List<File> foundFiles=new ArrayList<File>();

FileFilter customFilter=new FileFilter() {
    @Override
    public boolean accept(File pathname) {

        if(pathname.isDirectory()) {
            pathname.listFiles(this);
        }
        if(pathname.getName().endsWith("bla.xml")) {
            foundFiles.add(pathname);
            return true;
        }
        return false;
    }

};      
//rootFolder here represents a File Object pointing the root forlder of your search 
rootFolder.listFiles(customFilter);

When the code is run, you'll get all the found ocurrences at the foundFiles List.

Solution 4

Here is code based on bestsss' answer:

    Enumeration<URL> en = getClass().getClassLoader().getResources(
            "META-INF");
    List<String> profiles = new ArrayList<>();
    while (en.hasMoreElements()) {
        URL url = en.nextElement();
        JarURLConnection urlcon = (JarURLConnection) (url.openConnection());
        try (JarFile jar = urlcon.getJarFile();) {
            Enumeration<JarEntry> entries = jar.entries();
            while (entries.hasMoreElements()) {
                String entry = entries.nextElement().getName();
                System.out.println(entry);
            }
        }
    }

Solution 5

MRalwasser, I'd give you a hint, cast the URL.getConnection() to JarURLConnection. Then use JarURLConnection.getJarFile() and voila! You have the JarFile and you are free to access the resources inside.

The rest I leave to you.

Hope this helps!

Share:
214,421
MRalwasser
Author by

MRalwasser

Updated on March 05, 2020

Comments

  • MRalwasser
    MRalwasser about 4 years

    How can I use ClassLoader.getResources() to find recursivly resources from my classpath?

    E.g.

    • finding all resources in the META-INF "directory": Imagine something like

      getClass().getClassLoader().getResources("META-INF")

      Unfortunately, this does only retrieve an URL to exactly this "directory".

    • all resources named bla.xml (recursivly)

      getClass().getClassLoader().getResources("bla.xml")

      But this returns an empty Enumeration.

    And as a bonus question: How does ClassLoader.getResources() differ from ClassLoader.getResource()?

  • bestsss
    bestsss about 13 years
    There is no way to recursively search through the classpath... Sure, it's possible. For example: URLClassLoader.getURLs(). Open the JarFile, iterate through.
  • MRalwasser
    MRalwasser about 13 years
    @bestsss : what if the URL does not point to a JarFile, but to a directory?
  • Joachim Sauer
    Joachim Sauer about 13 years
    @bestsss, @MRalwasser: or even worse, if it points to a HTTP url?
  • bestsss
    bestsss about 13 years
    ;) well, the classloader can be just simple subclass of java.lang.ClassLoader w/o the URL part at any rate. It can generate the classes in the memory and so on. It might have very custom getResource, etc. @MRalwasser, if it points to some file system you process it like that, it's a simple scenario. @Joachim, besides applets nowadays there are not cases the files are kept remotely. The URL can be anything but as long as it is JAR one, you cant retrieve the jar from http.--What I told doesn't hold true always and it was not meant like universal solition,however it covers over 98% of the cases.
  • MRalwasser
    MRalwasser about 13 years
    I doubt that this will work in any cases for every type of ClassLoaders (e.g. classes within jar-files)
  • cn1h
    cn1h about 12 years
    I tested, I get an exception: java.lang.IllegalArgumentException: URI is not hierarchical. You cannot create a File object from an opaque URI like "jar:..."
  • Alex Pritchard
    Alex Pritchard over 10 years
    The structure of this kind of confused me at first. It seems as though the only purpose of getClass().getClassLoader().getResources("META-INF") is to get a reference to some arbitrary file in the jar, which we then use to get the JarURLConnection. I originally thought this would iterate over files in the META-INF directory, but it actually iterates over every single file in the entire jar.
  • Readren
    Readren about 8 years
    It doesn't work for me. It shows only the contents of the first jar file J in the class path such that J contains an entry that starts with the path given to getResources(path) method. So, the result of your code snippet is incomplete and depends on the order of the elements in the class path. So, @krock was right when he said that There is no way to recursively search through the classpath
  • m0skit0
    m0skit0 almost 8 years
    What do you mean by URL.getConnection()? I can't find this method anyway.
  • MatrixManAtYrService
    MatrixManAtYrService over 6 years
    If you're trying resolve resources within a jar, and you're not sure what the pattern should look like, run jar tf myjar.jar. I ended up with PathMatchinResourcePatternResolver("definitions/**/*.json").
  • WestCoastProjects
    WestCoastProjects over 4 years
    what does classpath* actually mean: is that intended to be replaced with the fully qualified contents of the current classpath to search in?
  • rec
    rec over 4 years
    If there is more than one JAR containing the same resource, then classpath:my/resource.xml is supposed to thrown an error while classpath*:my/resource.xml would return both. For more details, see e.g. tech-tauk.blogspot.com/2010/04/…
  • Buddha Buddy
    Buddha Buddy over 4 years
    Sorry for the necro comment, but this really does work. The only issue was that the "if" statement should be a "while".