Why use a ClassLoader to read a resource - java

I encountered a case where I need to use ClassLoader:
I have a XML file which specifies the configuration detail for sql, and I want to load it into a configuration class. The first step is to load what is in the XML into an Inputstream.
public class Resources{
public static InputStream getResourceAsStream(String path){
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}
I only know vaguely what is a classloader: It loads classes into JVM. It is not clear to me at all why one would use classLoader here. Can't we just read what is in path directly? My guess is that this might have something to do with the timing of when one wants to load the resource.

A project is composed of two things:
Code compiled into .class file
Ressources (any file such as properties, xml...)
Then it is packaged. The packaging can be mainly:
a JAR
a directory
A ClassLoader is what is capable to access to packaged projects (jars, directories...). The main ClassLoader is accessing jars and directories specified in the classpath, but additional ClassLoader may be added at runtime. For example, on an application server, where you can deploy new packaged applications at runtime, for every application a ClassLoader will be created.
That's why, to access ressources from a packaged project, you need to use a ClassLoader (even the name is not clear about ressources).
If you want to access a ressource packaged together with your class in the same project, you get the ClassLoader of your class so you are sure it can access the ressources of the same project.
The most typical ClassLoader is java.net.URLClassLoader, which takes a list of URLs (local or remote JARs, directories...) such as the classpath, and look into every URL to search for .class files or ressources files.
To sum up, you can see a ClassLoader as a list of locations where to search files, either .class to load classes, or any other type of file as ressources.

Related

adding resource files to karaf classpath

I am working on a project using karaf 4.0.5, and osgi. We have client side code to invoke a REST API, which requires to load 3 "*.properties" files. I have been given a client jar that I'm using to invoke the server side classes and methods (containing code that I cannot update). The required property files are present in the provided client jar, but their code still doesn't locate and load them.
On debugging my pax exam I found the below possible reasons for it not loading resource files from the jar.
the code to load the files seems to attempt to load resources only from the Bundle Classloader, and
it calls the "getResource()" method instead of the "getResourceAsStream()" method.
Alternatively, I tried adding the resources to a directory on my file system, and appending the classpath with the directory's location, as in:
"-cp .;C:/Users/abcUser/Desktop/resourceFolder/;"
(windows 7, classpath entry added as a VM argument while executing pax exam using junit 4+ from eclipse) -> this doesn't work either and its still unable to locate these files.
What other options do I have so the Bundle Classloader locates these files?
Note: We already have a bunch of other *.cfg files whose contents are loaded into beans using blueprint and are registered in the containers, but that's not what I need to do here. During runtime, these files should be available to the BundleClassloader, and should be retrieved by the "getResource()" method.
Update: Following the below portion of the accepted answer, the properties files were successfully loaded by the application.
Another thing to check is whether the client code is actually using the bundle classloader when trying to load these resources. In some cases the code tries to be clever by using the Thread Context Classloader, which would need to be set appropriately before calling the client code.
The code from the client jar was exactly as guessed: the resource loading was happening using the Thread.currentThread().getContextClassLoader(). I was able to set the ContextLoader to the CustomAbstractProcessor's classloader and it now loads the properties files from that bundle's classpath!
ClassLoader previousCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(CustomAbstractProcessor.class.getClassLoader());
try {
//REST call to server using classes and methods from provided client jar.
}
finally {
Thread.currentThread().setContextClassLoader(previousCL);
}
I have been given a client jar that I'm using to invoke the server side classes and methods (containing code that I cannot update). The required property files are present in the provided client jar, but their code still doesn't locate and load them.
If this client jar is running as an OSGi bundle then it should be able to find resources using its own class loader (the bundle class loader) if (and only if) the resources are on the bundle's classpath.
The default classpath for an OSGi bundle is . i.e. the root of the bundle. This can be overridden using the Bundle-ClassPath manifest header and used to one or more locations in the bundle.
One possibility is that the client bundle has a different classpath set and the properties files are not on it.
Another possibility is that the properties files are on the classpath, but that the locations don't match what's expected, e.g. the code is looking for foo.properties and the file is at `/props/foo.properties'
it calls the getResource() method instead of the getResourceAsStream() method.
getResourceAsStream() is just a null-safe version of getResource().openStream().
What other options do I have so the Bundle Classloader locates these files?
Another thing to check is whether the client code is actually using the bundle classloader when trying to load these resources. In some cases the code tries to be clever by using the Thread Context Classloader, which would need to be set appropriately before calling the client code.

Cannot load resource in jar

I am trying to load a resource in a jar, here is the exported jar:
'main' is the package with all my classes, and in one of those classes I am trying to load the background.png file. In my Eclipse project I put the resources under a "res/" folder, which I added to the build path to include it. When I try to use
new File("background.png");
It can't find the file.
When I use
MyClass.class.getClass().getClassLoader().getResource("background.png");
It still can't find the file.
Files packaged in a jar can't be accessed as File objects.
When you try
MyClass.class.getClass().getClassLoader().getResource("background.png");
you are actually using the ClassLoader of java.lang.Class and not of main.MyClass which may not be able to find the resource (in case it is the system classloader). Try
MyClass.class.getClassLoader().getResource("background.png");
instead.

Java resource from class vs Thread

what is the difference between
getClass().getResource("some-resource-file.txt")
vs
Thread.currentThread().getContextClassLoader().getResource("some-resource-file.txt")
I have resources in src/test/resources & I am trying to access them from Unit test. It's a typical maven style directory structure.
I was expecting both to behave identical. But it's not., getClass().getResource() doesn't fetch the resource where as from Thread I am able to fetch the resource.
So how do they differ ?
Let's say you're developing a library and the library jar is placed into a web container's classpath.
Now let's say a webapp, using this library, is deployed in the container.
The webapp will have its own class loader, using WEB-INF/classes and WEB-INF/lib/*.jar as its classpath. And the container, for each request coming to your webapp, will set the current thread classloader to the class loader of the classpath.
When your library code uses getClass().getResource(), it will load the resource using the classloader used to load the library classes. It will thus use the container's class loader, and will thus use the resources in your library's jar and in the other libraries used to start the container.
If your library code uses Thread.currentThread().getContextClassLoader() instead to load the resource, it will use the classloader associated with the current thread, and will thus load the resources from the webapp's class loader, looking for the resource in WEB-INF/classes and in the jars inside WEB-INF/lib.
The latter can be what you want. For example, if you're designing a logging library (please don't), the logger will be able to read a different configuration file for each webapp, instead of having a single config shared by all the webapps.
Regarding the way the two methods look for resources, they all finally delegate to a ClassLoader to load the resource. But loading it via a Class will treat relative paths as relative to the invoked class, whereas loading it via a ClassLoader expects a path starting at the root of the package tree. Suppose your class is in the package com.foo, then
MyClass.class.getResource("hello.txt")
is equivalent to
MyClass.class.getResource("/com/foo/hello.txt")
and is equivalent to
MyClass.class.getClassLoader().getResource("com/foo/hello.txt");
There is a special case getting the first class running (which is why you have to declare the main() method as static with an array of strings as an argument).
Once that class is loaded and is running, future attempts at loading classes are done by the class loader. At its simplest, a class loader creates a flat name space of class bodies that are referenced by a string name. Each class in Java uses own classloader to load other classes. So if ClassA.class references ClassB.class then ClassB needs to be on the classpath of the ClassLoader of ClassA, or its parents.
The thread context ClassLoader is a special one in that it is the current ClassLoader for the currently running thread. This is useful in multi-classloader environments. An object can be created from a class in ClassLoader C and then passed to a thread owned by ClassLoader D. In this case the object needs to use Thread.currentThread().getContextClassLoader() directly if it wants to load resources that are not available on its own ClassLoader .

How to read properties file in web application? [duplicate]

This question already has answers here:
Where to place and how to read configuration resource files in servlet based application?
(6 answers)
Closed 6 years ago.
Properties file location is WEB-INF/classes/auth.properties.
I cannot use JSF-specific ways (with ExternalContext) because I need properties file in a service module which doesn't have a dependency on a web-module.
I've already tried
MyService.class.getClassLoader().getResourceAsStream("/WEB-INF/classes/auth.properties");
but it returns null.
I've also tried to read it with FileInputStream but it requires the full path what is unacceptable.
Any ideas?
Several notes:
You should prefer the ClassLoader as returned by Thread#getContextClassLoader().
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
This returns the parentmost classloader which has access to all resources. The Class#getClassLoader() will only return the (child) classloader of the class in question which may not per se have access to the desired resource. It will always work in environments with a single classloader, but not always in environments with a complex hierarchy of classloaders like webapps.
The /WEB-INF folder is not in the root of the classpath. The /WEB-INF/classes folder is. So you need to load the properties files relative to that.
classLoader.getResourceAsStream("/auth.properties");
If you opt for using the Thread#getContextClassLoader(), remove the leading /.
The JSF-specific ExternalContext#getResourceAsStream() which uses ServletContext#getResourceAsStream() "under the hoods" only returns resources from the webcontent (there where the /WEB-INF folder is sitting), not from the classpath.
Try this:
MyService.class.getClassLoader().getResourceAsStream("/auth.properties");
Reading files with getResourceAsStream looks on the classpath to find the resource to load. Since the classes directory is in the classpath for your webapp, referring to the file as /auth.properties should work.
ResourceBundle (http://download.oracle.com/javase/6/docs/api/java/util/ResourceBundle.html) resolve most of the problems with a relative/absotule path for Properties Files.
It uses the the Resource class and point it to a Dummy Class to make reference to a properties file.
For example:
You a have file called MAINProperties.properties and inside it there is a property:
mail.host=foo.example.com
Create a Dummy Class called MAINProperties without nothing.
Use the following code:
ResourceBundle.getBundle("com.example.com.MAINProperties").getProperty("mail.host")
And That's it. No InputStreams Required.
P.D. Apache Commons has a Library Called Apache Commons Configuration that has a lot of capabilities (reloadable files, multiple domain types) that could be used in combination of the above.

getResourceAsStream not loading resource

The project that I am currently working on utilizes an old application contained within a .jar file. One of the responsibilities of this application is that it updates the database when changes to the configuration files are made. Every time I try to run this file (which is a simple Ant Task extension), I get an exception thrown early in the process. I decompiled the Java file responsible, and found that the exception being thrown happens here. I do not know what the issue is, as "hibernate.cfg.xml" is contained within the same .jar file as the .class throwing the exception.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream in = loader.getResourceAsStream("hibernate.cfg.xml");
if (in == null) {
throw new RuntimeException("Couldn't find built in hibernate config");
}
If anyone has any ideas, even pointing me in the right direction, I would be grateful.
Of course, any solution will have to be external, as the client already has this build of the program in production use.
Are you loading it from the root dir and you need "/hibernate.cfg.xml" instead of just hibernate.cfg.xml?
getResourceAsStream() expects the file to be in the same package as the Class that was the origin of the ClassLoader. Is your file in the right package?
Doh. Didn't read the question fully. Please ignore this.
try
InputStream in = YourClass.class..getResourceAsStream("hibernate.cfg.xml");
this will work if the class is in the same jar as the cfg file.
edit:
if you can't rebuild the application, you will need to add the jar to the bootstrap classloader. this is very ugly.
you can do it by running the jvm with (play with the exact arguments, you may need to add rt.jar from your jre to it as well).
-Xbootclasspath your.jar
your problem is that the code is using the classloader that loaded the Thread class, which is most likely the bootstrap classloader. and that you are now in a more complex environment (app server?) that loads your jar using a different classloader. so the bootstrap classloader can't find your resource.
It is possible that the classloader cannot open files contained in the jar file. Try one of two things 1) try extracting the jar file and running it from the file system, or 2) if the jar manifest has a ClassPath entry, try extracting the hibernate.cfg.xml into a directory in the classpath and see if it runs.
Apparently the hibernate.cfg.xml file isn't located in the source root of the JAR file, but it is instead placed inside a package. You'll need to specify the package path in the resource name as well.

Categories

Resources