Wednesday, July 30, 2014

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 file, 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 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 icons directory of your project, how do you load them? Answer is by using java.lang.Class' getResource() and getResourceAsStream() method. These method accepts path of resource as String and returns 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 its a non-static method, on the other hand, if you don't have any object, you can simply use .class with name of any class e.g. Sample.class will give you reference of java.lang.Class. These methods are available from JDK 1.1 and you can even use them anywhere you have access to core Java library. If you are creating J2ME games or application, you can use these method to load icons and tiles for your game, and all other resource for your application as well.


How does getResourceAsStream works

Internally this method delegate 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 path of 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 path 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 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 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;
    }

How to load Resources from ClassPath in Java with Example
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 following example, we have a Eclipse project, and I have created a properties file called app.properties inside 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 output of getResourceAsStream() before using it, defensive programming is there just because of this kind of methods.


Java Program to load Resource from Classpath

Here is our complete Java program to load images, resources, text file or binary file from classpath in Java, resource can be anything, what is important is that it 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

If you look closely you will find that we have used both getResource() and getResourceAsStream() method to load resource from classpath in Java, in this case just properties file. First example looks more cleaner than second example because we don't need to open an explicit stream, getResourceAsStream() method returns an InputStream, which can be used anywhere. That's all on how to load resources from class-path in Java. 

5 comments :

Anonymous said...

Good post........

Jitendra Arethiya said...

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

Anonymous said...

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

Javin Paul said...

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.

Javin Paul said...

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

Post a Comment