Preferred Way of Loading Resources in Java

Preferred way of loading resources in Java

Work out the solution according to what you want...

There are two things that getResource/getResourceAsStream() will get from the class it is called on...

  1. The class loader
  2. The starting location

So if you do

this.getClass().getResource("foo.txt");

it will attempt to load foo.txt from the same package as the "this" class and with the class loader of the "this" class. If you put a "/" in front then you are absolutely referencing the resource.

this.getClass().getResource("/x/y/z/foo.txt")

will load the resource from the class loader of "this" and from the x.y.z package (it will need to be in the same directory as classes in that package).

Thread.currentThread().getContextClassLoader().getResource(name)

will load with the context class loader but will not resolve the name according to any package (it must be absolutely referenced)

System.class.getResource(name)

Will load the resource with the system class loader (it would have to be absolutely referenced as well, as you won't be able to put anything into the java.lang package (the package of System).

Just take a look at the source. Also indicates that getResourceAsStream just calls "openStream" on the URL returned from getResource and returns that.

Groovy: Preferred Way to Handle Classpath Resource Loading

Answers in turn;

1:

String xsd = this.getClass().getResource('/xsd/file.xsd').text

And 2:

No. But there's a library that makes it easy

Best way to handle resource files in Java so that they work in Eclipse and when packaged into a jar?

I would recommend using Apache Maven. This tool can automatically JAR up your project and include your resources. They have a quick tutorial on Maven in 5 minutes. Then all you need to do is place your resources in src.main.resources and it should automatically included in the jar (see the Getting Started Guide) for more details.

Hope this helps!

Update

As for getting the resource from here, have you tried getResourceAsStream() from the Java file?

java - How to have a resource file

In general, unless you have good reason to, you probably don't need to overwrite maven's default configuration.

Maven uses a "convention over configuration" philosophy, meaning it:

  • assumes sensible default values for properties
  • allows you to overwrite this if you need to

So, if you want a resource folder, maven provides one for you by default (assuming you follow the default project structure)

You should have a resource folder at src/main/resources, with your Java code in src/main/java/<package-name>. This is the default structure that maven expects.

From there you can get a reference to this by using Class.getResource(). For example, if you have a file in src/main/resources/file.txt, you can access it with getClass().getResource("/file.txt") (note the leading slash).

However, if you want to modify this during runtime (perhaps if a user wants to save new details), you should not use resources. Instead, consider one of the following:

  • File in current directory - just use new File("config.txt") and read/write to that
  • File in user home directory - use new File(System.getProperty(user.home), "config.txt"). On Windows for example this creates a file at C:/users/<username>/config.txt.
  • Database - If your application already uses a database, it can be a good place to store a lot of configuration options, although, based on your question, I imagine this is not suitable for you.

How to Properly Get a File from resources Folder

Test.class.getClassLoader(); obtains a reference to the ClassLoader class from the Class' method public ClassLoader getClassLoader() - see private final ClassLoader classLoader below.

Since you are accessing the ClassLoader class from an object of that class, you're not accessing it in a static way.

From Class.java, Java SE 1.7:

@CallerSensitive
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}

// Package-private to allow ClassLoader access
ClassLoader getClassLoader0() { return classLoader; }

// Initialized in JVM not by private constructor
// This field is filtered from reflection access, i.e. getDeclaredField
// will throw NoSuchFieldException
private final ClassLoader classLoader;

In order to access the method in a static way, it has to be called from the class which declares it as static if you want to get rid of the warning:

ClassLoader.getSystemResource("test.html").getFile()

To avoid the NPE the test.html file should be under your source folder.


To respond to your comment, using a method which returns other than a URL solves your problem - see Reading a resource file from within jar.

public class Test {

public static void main(String[] args) {
StringBuilder contentBuilder = new StringBuilder();

InputStream is = Test.class.getResourceAsStream("/test.html");
try {
String sCurrentLine;

BufferedReader buffer = new BufferedReader(new InputStreamReader(is));
while ((sCurrentLine = buffer.readLine())!=null)
contentBuilder.append(sCurrentLine);
buffer.close();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}

System.out.println("content=" + contentBuilder.toString());
}
}

Loading resource files within a jar

This snippet of code worked for me:

Clip clip = null;
ClassLoader cl = this.getClass().getClassLoader();
AudioInputStream ais;
URL url = cl.getResource("com/example/project/assets/TestSound.wav");
System.out.println(url);
try {
ais = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(ais);
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}

The important thing is not adding the /src/ folder to the class path.

The critical change is changing cl.getResource("/com/example/project/assets/TestSound.wav") to cl.getResource("com/example/project/assets/TestSound.wav");
because /com/... indicates that the path is absolute, whereas com/... indicates that the path is relative.

For example,

System.out.println(new File("/Test.File").getAbsolutePath());
System.out.println(new File("Test.File").getAbsolutePath());

return

/Test.File
/Users/alphadelta/Documents/Workspace/TestProject/Test.File

respectively.

The first File created is created using "/Test.File", which is absolute. The second is created using "Test.File", which is relative to the project root in eclipse.

Class.getResource and ClassLoader.getSystemResource: is there a reason to prefer one to another?

There are several ways to load resources, each with a slightly different meaning -

ClassLoader::getSystemResource() uses the system classloader. This uses the classpath that was used to start the program. If you are in a web container such as tomcat, this will NOT pick up resources from your WAR file.

Class<T>#getResource() prepends the package name of the class to the resource name, and then delegates to its classloader. If your resources are stored in a package hierarchy that mirrors your classes, use this method.

ClassLoader#getResource() delegates to its parent classloader. This will eventually search for the resource all the way upto the system classloader.

If you are confused, just stick to ClassLoader#getResource()

Using the ClassLoader method to retrieve all resources under classes as Input Streams

You can do that with some tricks :)

Get the resource as URL, extract the protocol :

  • file protocol - get the URL path and you have a folder, scan for files.
  • jar/zip protocol - extract the jar/zip path and use JarFile to browse the files and extract everything under your path/package.


Related Topics



Leave a reply



Submit