getResourceAsStream in dynamically loaded jar - java

I kow there are dozens (if not hundreds) questions about Java's getResource/getResourceAsStream but i did not find any answers to my problem.
I load jars dynamically with: (String path is given)
File jar = new File(path);
URL url = new URL("file", "localhost", jar.getAbsolutePath());
URLClassLoader loader = new URLClassLoader(new URL[] { url });
c = loader.loadClass(name);
Then within the jar i try to load a resource in the jar. This resource clearly exists in the jar and the whole procedure works if I just run the whole thing with a class loader in Eclipse. In a jar it does not work.$
getClass().getResourceAsStream("resource.dat");
I tried every possible combination of /packageName/resource.dat, packageName/resource.dat, /resource.dat and resource.dat. They all throw a stream closed Exception.
The I tried debugging and ended up printing the URL of these files loaded via getClass().getResource(path)
This led to following URL and it does not look normal to me. Is it supposed to say "localhostC:..."?
jar:file://localhostC:\Users\******\export.jar!/packageName/resource.dat
Converting this URL also throws an Exception (URISyntaxException).
Is this URL really broken or am I just doing something wrong?

Try changing the line:
URL url = new URL("file", "localhost", jar.getAbsolutePath());
to
URL url = new URL("file", null, jar.getAbsolutePath());
The host parameter in the URL constructor is not applicable in this case.

First, the File class has a toURI() method, so the preferred way to get a URL pointing to a file is:
URL url = new File(path).toURI().toURL();
So using this, the class loader:
File jar = new File(path);
URLClassLoader loader = new URLClassLoader(new URL[] { jar.toURI().toURL() });
Next, when you want to load a resource for the jar, use a Class that is originating from the jar, so if you already loaded the Class c:
Class<?> c = loader.loadClass(classPathAndName);
URL resource = c.getResource("data.txt");
This will result in something like:
jar:file:/C:/test/testjar.jar!/testpkg/data.txt
Alternatively you can use the ClassLoader to get the resource like:
loader.getResoure(packageAndResourceName);
But note that this quoting from the javadoc:
This method will first search the parent class loader for the resource; if the parent is null the path of the class loader built-in to the virtual machine is searched. That failing, this method will invoke findResource(String) to find the resource.

Related

URLClassLoader trying to get a class from external jar always returning null

I am trying to load a class from a jar that is sitting in a directory.
The structure of the class I am trying to grab is as follow:
myapp.jar
|__META-INF
|__com
|__myapp
|__config
|__PlumberConfig.class
This is how I tried to grab the class:
File file = new File("C:/_workspace/_jar/myapp.jar");
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
URLClassLoader ucl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
LOG.info("URLClassLoader: {}", ucl.getName()); // here always null
Class c = ucl.loadClass("com.myapp.config.PlumberConfig");
The result of new URLClassLoader() is always null hence I cannot go further from this point.
I double-checked everything (the path, name of the jar, and location of the class itself) but seems I am still missing something.
Your code is working but the method getName() is not defined on URLClassLoader. Try ucl.getURLs() instead. Than it should work as expected.

Java loading a resource from outside the classpath (pentaho)

I am a couple of weeks into Java programming and are trying to get the pentao report definition from a folder that is outside the class path, but have so far been unsuccessful.
The following successfully loads the definition from a folder contained in the class path, however I am unable to figure out how to get it from an absolute path outside the class path:
// Parse the report file
final URL reportDefinitionURL = classloader.getResource("some/path/inclass/Sample1.prpt");
final ResourceManager resourceManager = new ResourceManager();
final Resource directly = resourceManager.createDirectly(reportDefinitionURL, MasterReport.class);
return (MasterReport) directly.getResource();
I am not sure if this is pentaho-specific or a generic Java issue, but how can I get the file definition based on a absolute path (linux) which is not located in the class path such as
"/usr/share/pentaho/Sample1.prpt" ?
I have tried this:
File file = new File("/usr/share/pentaho/");
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
final URL reportDefinitionURL = cl.getResource("Sample1.prpt");
I have also tried including the external path at runtime but that does not appear to work either:
java -cp ./lib/*:/usr/share/pentaho/*.prpt ...
Any assistance is greatly appreciated !
Default system class loader will look for resources only in the class path. If u know the absolute path, it would be easier to create the URL object directly:
URL reportDefinitionURL = new URL("file:/tmp/Sample1.prpt");
I am not familiar with the pentaho api's, so not sure how the following api works.
resourceMgr.createDirectly(reportDefinitionURL, MasterReport.class);
In last line it is returning an instance of "MasterReport", so "MasterReport.class" should be there in your class path.
EDIT:
I just looked at this pentaho wiki - http://wiki.pentaho.com/display/Reporting/Using+the+DocumentBundle+Reader+and+Writer
Looks like the createDirectly() apis can take a file object
File reportDefinitionFile = new File ("/tmp/Sample1.prpt");
resManager.createDirectly(reportDefinitionFile, MyClass.class);

Load class within Jar file and get it's path

I know there are already a lot similar questions here, but I couldn't get any smarter form them. I want to load a class inside a jar file, at this point this is no problem, but when I want to pass the path to my own ClassLoader it throws an exception it cannot find the class. Is it possible to load a class inside a jar using an absolute path? For instance,Class cls = loader.loadClass(/path/MyPlugin.jar/MyPlugin.class);
But when I do this:
File test = new File("path/plugins/MyPlugin.jar/MyPlugin.class");
System.out.println(test.exists());
It prints out false. I tried using MyPlugin.jar!/MyPlugin.class or MyPlugin.jar.jar!MyPlugin.class which i've seen sometimes on the web, even though i don't really know what it means...
When I do this it finds the class:
URLClassLoader loader = new URLClassLoader(new URL[] { "path/plugins/MyPlugin.jar" });
Class cl = loader.loadClass("MyPlugin");
But now, how can I receive the path? Something like URL url = cl.getResource("MyPlugin"); (which gives back a null)
You can obtain URLs to classpath resources using ClassLoader.getResources. To find a jar with specific class, you may use the following
URL url = classLoader.getResource("com/example/SomeClass.class");
JarURLConnection connection = (JarURLConnection) url.openConnection();
JarFile file = connection.getJarFile();
String jarPath = file.getName();
where classLoader is any classloader capable of finding the class you want to load. If the jar is a part of your application's classpath, you may use system classloader:
ClassLoader classLoader = ClassLoader.getSystemClassloader();
Otherwise, you need to know the jar file location beforehand, and create an instance of URLClassLoader, passing the jar in the constructor:
ClassLoader classLoader = new URLClassLoader(new URL[]{new URL("path/to/the/jar/file.jar")});
and then use it to load your class.
If you want to access a file in your jar file you have to put it in the same directory as the class files are.
For example if Main.class is in bin/my/pkg/Main.class you can store your MyPlugin.class in the same directory (bin/my/pkg/MyPlugin.class) and then, if you want to access that file use
URL url = Main.getResource("MyPlugin.class");
Which uses the location of Main.class in your project as root.
Hope it helps!

Loading a custom class from arbitrary location

I am having some issues with Class Loading in Java. Inside my project I am trying to dynamically load a class from anywhere. However, I am currently failing at loading a hard-coded one and am still clueless after 6 hours of googling and stack-overflow checking.
I am suspecting that there is an issue with the package name of the class I am loading.
My goal is to load the Class LoadAClass.java in the project/resources/dynamicFolderNonInClassPath/loadThis directory. Since I am setting my URLClassLoader to the folder above, its package has been set to
package loadThis;
public class LoadAClass{
static{
System.out.println("I am loaded");
}
(...)
}
However I keep getting a class not found exception.
File file = new File("C:/Users/Robert/Documents/workspace/project/resources/dynamicFolderNonInClassPath/");
if (!file.exists()) System.out.println("typo!"); //debug print
URL url = file.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[]{url});
String classToBeLoaded = "loadThis.LoadAClass";
Class classy = loader.loadClass(classToBeLoaded);
System.out.println(classy.getCanonicalName()); //debug print
I have tried different combinations, like setting the URLClassLoader to the file directly or giving the full /resources/dynamicFolderNonInClassPath/loadThis as URL but how so far no success.
Someone in christmasy mood and seeing the problem? There seems to be some misunderstanding on my part regarding this functionality and I'd like to see it fixed.
you are missing a forward slash '/'
File file = new File("C:/Users/Robert/Documents/workspace/project/resources/dynamicFolderNonInClassPath");
if (!file.exists()) System.out.println("typo!"); //debug print
URL url = new URL("C:/Users/Robert/Documents/workspace/project/resources/dynamicFolderNonInClassPath/");
URLClassLoader loader = new URLClassLoader(new URL[]{url});
String classToBeLoaded = "loadThis.LoadAClass";
Class classy = loader.loadClass(classToBeLoaded);
System.out.println(classy.getCanonicalName()); //debug print
See the third line
A .java file isn't a class file. Have you compiled it? You would need a LoadAClass.class file in a "loadThis" directory with your class loader pointing at the directory that contains "loadThis".

have a file in the classloader's root path, how to create it

File f = new File(path)
How to give the path parameter in this case?
You mean something like this?
URL resource = Thread.currentThread().getContextClassLoader().getResource("config.properties");
File f = new File(resource.toURI());
In principle, you need to know from where the classloader loads its resources. This is classloader-dependent, and most types of classloader don't use files at all. If you have an URLClassLoader (which fortunately is quite often), you can ask it about its URLs, and look if there is one file: URL. Then use this URL as a base.
If your Classloader has no file: URL, obviously you have no chance.
But I think most probably you are not doing the right thing - what do you really want to do?
You could use a better option and go for java.net.URLClassLoader.
This class loader is used to load classes and resources from a search path of URLs referring to both JAR files and directories.
A URLClassLoader can be used to load classes in any directory.
Check out this example
// Create a File object on the root of the directory containing the class file
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
Also, have a look at File ClassLoader in Java

Categories

Resources