How to use ClassLoader.getResources() correctly? [duplicate]
Asked Answered
F

5

95

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()?

Fantail answered 4/3, 2011 at 12:36 Comment(3)
@Andrew, a lot of frameworks iterate for some files based on name or extensions in the archives to automate some processes, like finding the ActionBeans of Stripes, or the hbm.xml files for hibernate.Icao
See #1429672Singlehanded
The reson why it confuses you is that getResources works on a class loader which can have multiple JARs in the classpath. So if you have multiple JARs with the same resource, you get all. However it is NOT intended to search inside directories. With getResources("META-INF") you get all META-INFO directories in the search path of the CL and if the CL is a single jar file class loader, you at most get one entry.Fluting
A
25

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.

Around answered 4/3, 2011 at 12:45 Comment(4)
There is no way to recursively search through the classpath... Sure, it's possible. For example: URLClassLoader.getURLs(). Open the JarFile, iterate through.Icao
@Icao : what if the URL does not point to a JarFile, but to a directory?Fantail
@bestsss, @MRalwasser: or even worse, if it points to a HTTP url?Worriment
;) 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.Icao
W
45

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");
Wain answered 24/6, 2013 at 19:41 Comment(3)
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").Trembles
what does classpath* actually mean: is that intended to be replaced with the fully qualified contents of the current classpath to search in?Pimply
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/…Wain
A
25

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.

Around answered 4/3, 2011 at 12:45 Comment(4)
There is no way to recursively search through the classpath... Sure, it's possible. For example: URLClassLoader.getURLs(). Open the JarFile, iterate through.Icao
@Icao : what if the URL does not point to a JarFile, but to a directory?Fantail
@bestsss, @MRalwasser: or even worse, if it points to a HTTP url?Worriment
;) 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.Icao
B
23

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.

Barren answered 4/3, 2011 at 13:3 Comment(2)
I doubt that this will work in any cases for every type of ClassLoaders (e.g. classes within jar-files)Fantail
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:..."Michelinamicheline
B
13

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);
            }
        }
    }
Bouldin answered 11/3, 2013 at 5:43 Comment(4)
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.Sherri
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, @Around was right when he said that There is no way to recursively search through the classpathPastorate
Sorry for the necro comment, but this really does work. The only issue was that the "if" statement should be a "while".Equalize
Does not seem to work with modern (Java 11) AppClassLoader - it will not iterate class path JARs.Hankering
I
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!

Icao answered 4/3, 2011 at 12:37 Comment(1)
What do you mean by URL.getConnection()? I can't find this method anyway.Sherbet

© 2022 - 2024 — McMap. All rights reserved.