Thursday, October 28, 2021

How to Load Resources from Classpath in Java with Example

Classpath in Java is not only used to load .class files, but also can be used to load resources e.g. properties files, images, icons, thumbnails, or any binary content. Java provides API to read these resources as InputStream or URL. Suppose, you have a properties file inside the config folder of your project, and you want to load that properties file, how do you do that? Similarly, you have icons and thumbnails for your web applications on the icons directory of your project, how do you load them? The answer is by using java.lang.Class' getResource() and getResourceAsStream() method. These methods accept the path of resource as String and return URL and InputStream respectively.

You can obtain a reference of Class by calling either getClass() method or by using class literal. If you have an object, then you can call getClass() because it's a non-static method, on the other hand, if you don't have an object, you can simply use .class with the name of any class like Sample.class will give you a reference of java.lang.Class.

These methods are available from JDK 1.1 and you can even use them anywhere you have access to the core Java library. If you are creating J2ME games or applications, you can use these methods to load icons and tiles for your game, and all other resources for your application as well.





How do getResourceAsStream works

Internally this method delegates the loading request of resource to its class loader. If you call getResourceAsStream() method on an object which is loaded by BootStrap ClassLoader then it will delegate it to ClassLoader.getSystemResourceAsStream(java.lang.String) method.

We pass the path of the resource to this method but rules for searching resources associated with a given class are implemented by the defining class loader of the class.
Since you can pass both absolute and relative paths to Class.getResourceAsStream(), but ClassLoader.getResourceAsStream() takes an absolute path, that's why an absolute resource name is constructed from the given resource name using the following algorithm :
If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'. Otherwise, the absolute name is of the following form:
modified_package_name/name where the modified_package_name is the package name of this object with '/' substituted for '.' ('\u002e').

This means the resource name passed to the method should look like /com/abc/config/app.properties if the app.properties is stored in the com.abc.config package instead of the current class's.



If you look at the code of java.lang.Class in Eclipse IDE by using short-cut Ctrl+T and typing java.lang.Class, you can see how this method works :

 public InputStream getResourceAsStream(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name);
}

This algorithm is implemented at the resolveName() method, as seen below :

    /**
     * Add a package name prefix if the name is not absolute Remove leading "/"
     * if name is absolute
     */
    private String resolveName(String name) {
        if (name == null) {
            return name;
        }
        if (!name.startsWith("/")) {
            Class c = this;
            while (c.isArray()) {
                c = c.getComponentType();
            }
            String baseName = c.getName();
            int index = baseName.lastIndexOf('.');
            if (index != -1) {
                name = baseName.substring(0, index).replace('.', '/')
                    +"/"+name;
            }
        } else {
            name = name.substring(1);
        }
        return name;
    }


The main problem comes while loading resource using getResourceAsStream() method is NullPointerException, because this method return null if its not able to find the resource. 

In the following example, we have an Eclipse project, and I have created a properties file called app.properties inside the config directory. 

Now to load that file, I just need to pass "app.properties", if I pass anything like "config/app.properties" or "/config/app.properties" getResourceAsStream() will return null, and code will subsequently throw NullPointerException as shown below :

Exception in thread "main" java.lang.NullPointerException
    at java.util.Properties$LineReader.readLine(Unknown Source)
    at java.util.Properties.load0(Unknown Source)
    at java.util.Properties.load(Unknown Source)
    at Test.main(Test.java:29)

to avoid this error you must check the output of getResourceAsStream() before using it, defensive programming is there just because of this kind of method.




Java Program to load Resources from Classpath

Here is our complete Java program to load images, resources, configuration files, property files, text files, or binary files from classpath in Java, Resources can be anything, what is important is that they must be accessible.

package test;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;

/**
 * Java Program to demonstrate how to load resources e.g. properties file from
 * classpath. There are two ways to load resources in Java, one by using
 * getResourceAsStream() and getResource() method from java.lang.Class. Main
 * difference between these two methods are that one returns an InputStream
 * while other returns a URL object.
 *
 * @author Javin Paul
 */
public class ResourceLoader{

    public static void main(String args[]) {

        // loading resource using getResourceAsStream() method
        InputStream in = ResourceLoader.class
                          .getResourceAsStream("app.properties");

        Properties config = new Properties();
        try {
            config.load(in);
            System.out.println(config.getProperty("name"));
            System.out.println(config.getProperty("version"));

        } catch (IOException e1) {
            e1.printStackTrace();
        }

        // loading resource using getResource() method
        URL resourceURL = Test.class.getResource("app.properties");
        Properties appConfig = new Properties();
        try {
            appConfig.load(resourceURL.openStream());
            System.out.println(appConfig.getProperty("name"));
            System.out.println(appConfig.getProperty("version"));

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Output:
SampleApp
1.0.0
SampleApp
1.0.0
You can see that data is loaded from the classpath in Java and properties and their values are printed on the console. 

How to load Resources from ClassPath in Java with Example

If you look closely you will find that we have used both getResource() and getResourceAsStream() methods to load resources from classpath in Java, in this case just properties file. 

The first example looks cleaner than the second example because we don't need to open an explicit stream, the getResourceAsStream() method returns an InputStream, which can be used anywhere. That's all on how to load resources from class-path in Java.

Other Java fundamental Tutorials

Thanks for reading this article so far. If you like this article then please share it with your friends on Facebook and Twitter, that means a lot for us and it will encourage us to create more free tutorials like this for you. 

8 comments:

  1. I am big fan of yours and your javarevisited. because of your blogs me and many friends got confidence in interview and able to clear my technical round. I really appreciate your work and time on this blogs.

    By executing above program and maybe i found two errors

    1. InputStream in = Test.class.getResourceAsStream("app.properties");
    Here what Test indicates? It should be current class name. I am using eclipse so it suggest me to import import junit.framework.Test;
    2. In above line, getResourceAsStream("app.properties"); should be replaced with getResourceAsStream("/app.properties"); because as per my knowledge it tries to search from your source directory. i.e. top level package of your all classes

    ReplyDelete
  2. comments "// loading resource using getResourceAsStream() method " and "// loading resource using getResource() method" should be swapped.

    ReplyDelete
  3. Hello @Jitendra, thanks for being a regular reader of my blog. First one is type, yes it should be ResourceLoader.class.getResourceAsStream() but second one is fine. When you give "app.properties" in Eclipse it will search from your project root directory. This should not be problem if app.properties is present in your project directory.

    ReplyDelete
  4. @Anonymous, good find, thanks for that. I have swapped the comment now.

    ReplyDelete
  5. In case, I need to modify the name or version in the property file, how can it be done directly to the file in the classpath?

    ReplyDelete
  6. Hi Javin, I find your blogs very descriptive and easy to understands. However, I have a question related to this.
    If two classes implement an interface, and my project wishes to load a property file to JVM can i load it using the interface reference?
    Example:

    Class A implements X{} and also
    Class B implements X{}

    I wish to load a resource that will be used by both A & B.
    Can I load it like (Please see below):

    X.class.getClassLoader().getResourceAsStream("config/app.properties");

    So that it can be used by both A & B
    ??
    This is my question, hoping to hear from you!

    ReplyDelete
  7. If classes A, B and C are loaded from the same class loader, and if the file you seek is stored alongside X interface.

    ReplyDelete
  8. Yes, I don't see a reason why you cannot, are you getting any issue? are you trying to load the resources from classpath or remote directory?

    ReplyDelete