URL to load resources from the classpath in Java

Viewed 325453

In Java, you can load all kinds of resources using the same API but with different URL protocols:

file:///tmp.txt
http://127.0.0.1:8080/a.properties
jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class

This nicely decouples the actual loading of the resource from the application that needs the resource, and since a URL is just a String, resource loading is also very easily configurable.

Is there a protocol to load resources using the current classloader? This is similar to the Jar protocol, except that I do not need to know which jar file or class folder the resource is coming from.

I can do that using Class.getResourceAsStream("a.xml"), of course, but that would require me to use a different API, and hence changes to existing code. I want to be able to use this in all places where I can specify a URL for the resource already, by just updating a property file.

14 Answers
URL url = getClass().getClassLoader().getResource("someresource.xxx");

That should do it.

From Java 9+ and up, you can define a new URLStreamHandlerProvider. The URL class uses the service loader framework to load it at run time.

Create a provider:

package org.example;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.spi.URLStreamHandlerProvider;

public class ClasspathURLStreamHandlerProvider extends URLStreamHandlerProvider {

    @Override
    public URLStreamHandler createURLStreamHandler(String protocol) {
        if ("classpath".equals(protocol)) {
            return new URLStreamHandler() {
                @Override
                protected URLConnection openConnection(URL u) throws IOException {
                    return ClassLoader.getSystemClassLoader().getResource(u.getPath()).openConnection();
                }
            };
        }
        return null;
    }

}

Create a file called java.net.spi.URLStreamHandlerProvider in the META-INF/services directory with the contents:

org.example.ClasspathURLStreamHandlerProvider

Now the URL class will use the provider when it sees something like:

URL url = new URL("classpath:myfile.txt");

I dont know if there is one already, but you can make it yourself easilly.

That different protocols example looks to me like a facade pattern. You have a common interface when there are different implementations for each case.

You could use the same principle, make a ResourceLoader class which takes the string from your properties file, and checks for a custom protocol of ours

myprotocol:a.xml
myprotocol:file:///tmp.txt
myprotocol:http://127.0.0.1:8080/a.properties
myprotocol:jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class

strips the myprotocol: from the start of the string and then makes a decision of which way to load the resource, and just gives you the resource.

An extension to Dilums's answer:

Without changing code, you likely need pursue custom implementations of URL related interfaces as Dilum recommends. To simplify things for you, I can recommend looking at the source for Spring Framework's Resources. While the code is not in the form of a stream handler, it has been designed to do exactly what you are looking to do and is under the ASL 2.0 license, making it friendly enough for re-use in your code with due credit.

In a Spring Boot app, I used the following to get the file URL,

Thread.currentThread().getContextClassLoader().getResource("PromotionalOfferIdServiceV2.wsdl")

If you have tomcat on the classpath, it's as simple as:

TomcatURLStreamHandlerFactory.register();

This will register handlers for "war" and "classpath" protocols.

Related