java urlclassloader usage.Is it needed only in some rare cases? - java

In which case one would use a URLClassLoader to load a class from a specific jar in a specified path?
E.g.
URL url = new URL("file:///path/to/customClasses.jar");
URLClassLoader pluginLoader = new URLClassLoader(new URL[] { url });
Class<?> cl = pluginLoader.loadClass("apackage.MyCustomClass");
If I wanted to use classes from customClasses.jar, I always thought that placing this jar in a path accesible from CLASSPATH is enough.
Then in my code just use apackage.MyCustomClass.
I think I have something missunderstood or missing here, so could someone please explain and give an example of when the above snippet of loading class this way is useful?
Thanks!

I would say that depending on the type of programming you are doing, the use of the URLClassLoader should be a very rare occurrence.
Typically you will use the class loader for loading in classes at runtime that you couldn't anticipate in advance.
A good example is if you build a tool that can be extended with plugins, and the plugins are loaded at runtime. For example, Eclipse.
If you have the jar available at compile time and are on a command line, add the needed jar file to your compile statement. For example,
javac -cp /path/to/lib/customClasses.jar MyClassThatReferencesCustomClasses
If you're using Eclipse, add the jar to your project, and right click on it and select add to build path.
Regards,
Will

Related

Reading Classes found in a Jar packaged in a Zipfile

I’ve spent a lot of time working on an assignment, “read a classfile found in a Jar stored contained in a Zipfile” and always return to the same ClassNotFoundException. I’m just seeking some guidance regarding how to proceed. I read many useful links on various sites, but continue to encounter problems either because a daunting task or I lack the knowledge.
Basically, my code successfully reads reads the Zipfile, creates a JarInputstream and finds my requested classfile. My problem, locating the file when the code executes the line, cl.loadClass(className) throwing the ClassNotFoundException.
URL jarUrl = new URL("jar:file:/E:/temp/ZipTest/JarTest_08262014/JarTest3.zip!/activation.jar!/com/sun/activation/viewers/ImageViewer.class");
URLClassLoader cl = URLClassLoader.newInstance(new URL[] {jarUrl });
className= "com/sun/activation/viewers/ImageViewer.class";
Class loadedClass = cl.loadClass(className);
Once I've loaded the class I can use the reflection methods to read the classfile. I wrote a class which works when reading jar files located in a directory.
I looked at OpenJDK javap code, but I don’t see any reflection calls. So, I’m lost at this point. Did I construct an incorrect URL?
Any suggestions?
I think you use the URLClassloader wrong.
Try to use a URL which points only to the JAR file.
Nested Jars are not allowed by default URLClassloader.
Further:
Don't specify the class's file.
Means:
className= "com/sun/activation/viewers/ImageViewer.class";
should be:
className= "com.sun.activation.viewers.ImageViewer";

Does a .class file on disk have to follow the same directory structure as its qualified name in Java for us to run it?

After reading about dynamic class loading (that is, loading a .class file from disk) I am getting a bit worried.
Let's say I have a file called MyClass.class that contains the class a.b.c.MyClass.
Assuming I now decide to move the file to C:\(my root folder in Windows), I'd like to dynamically load this class. Is that possible, at all? From what I understood it seems MyClass' path would always have to be of the form *a/b/c.MyClass.
Thus, the following bit of code doesn't seem to work:
URL[] urls = new URL[] { new File("C:\\").toURL() };
URLClassLoader classLoader = new URLClassLoader(urls);
Class<?> targetClass = classLoader.loadClass("a.b.c.MyClass");
Forcing us to put a .class file in a directory structure that reflects its full internal name is insane, IMO. Am I missing something?
A possible implication of this fact would be that if I decide to copy a couple of .class files into a temporary directory so I can perform some awesome wizardry over them, I'll have to replicate all their dirty paths in that same temporary directory, which is awkward, at best.
Does a .class file on disk have to follow the same directory structure as its qualified name in Java for us to run it?
Yes, if you are using the standard classloaders.
In theory, you could implement a custom classloader that used a different scheme for locating the class files. But there's a good chance that you would run into problems when (for example) debugging your code. So I wouldn't recommend it.

How to load all classes of a jar file at runtime?

According to this question, it is possible to load a class from a jar file with:
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { jarFileURL },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
How to load all classes contained in a jar file?
The class loader will load a .class file as soon as it's needed. If it's not needed, it won't be loaded.
Why do you think that your approach will be an improvement over what the class loader already does?
You will have to open it as zip file, go through the names, assume all entries ending in .class are class files and load them, ignoring nested classes and such.
But....why?
If you really want to read classes dynamically, leave that to the pros, who already implemented that. Implementing your own classloader is hard. Believe me. I already tried a few times. Use something like OSGi instead, which provides you dynamic class loading and much more.

How to get a resource in another jar

I have a jar embedded in a bundle that needs to fetch a resource packaged with it like so:
MyBundle
-\ src
-\lib
-\MyEmbeddedJar
-\src
-\SomeClass
-\someResource.xml
I am trying to access 'someResource.xml' from 'SomeClass' like so:
SomeClass.class.getResource( "someResource.xml" );
But I've had no luck. I've tried several variations with the CWD appended (eg: './someResource.xml') but I just can't get this resource to load.
I know that the "right" way is to use Activator to get hooks back to the proper classloader, but the embedded jar can be used in other projects, so I'd hate to have to add OSGi specific code to it just to get it to play nice with OSGi.
Is there any other way to load resources in OSGi agnostically of OSGi?
I Assume that SomeClass is inside the embedded jar (say, somejar.jar), and someResource.xml is in the outer jar, in a lib directory.
In this case, there is no way to get to that in a non-OSGi context. Let's look at both situations in isolation.
In OSGi
Your someResource.xml should very well be reachable using the regular (non-OSGi specific) resource loading mechanisms, provided that it is reachable from the Bundle-ClassPath. For instance, if you have the following manifest header,
Bundle-ClassPath: ., somejar.jar
you will be able to get to your resource using "lib/someResource.xml".
Notice the dot on the classpath: this means you can reach classes and resources from the root of the jar. If you forget that, you will only be able to get to classes and resources inside somejar.jar.
Not using OSGi
If you're not using OSGi, there is no (reasonably simple) way to get to classes and resources inside of the inner jar that I know of.
Your options
Depending on what you want your bundle to look like, you have two options now.
Is it really necessary that SomeClass is in an embedded jar? If so, you're at a loss, and you jar will only work using OSGi.
If you have the option to 'unpack' somejar.jar into your jar, you subvert the problem, and your jar can work in both situations.
Personally, I'd pick option 2.: unless you have resources that might overwrite each other when you 'merge' the jars, it is no problem at all to have a slight mess of resources inside your bundle.
My assumptions are that:
That I'm not quite sure I get how your description of the problem matches the diagram. Where is the *.jar file?
The bundle that is attempting to
access the embedded jar is the same
bundle that contains the embedded
jar.
Per the OSGi agnosticism, I am
assuming that the embedded jar is
not explicitly exposed as part of
the current bundle's classpath and
that it is not loaded as another
OSGi bundle.
If the jar in question is itself a resource of the current classloader, then you would first need to get the jar as a resource or as an InputStream, such as with MyBundleClass.class.getResourceAsStream("/pathToJar.jar"); then wrap it with a java.util.jar.JarInputStream. Then, continue to call getNextJarEntry() until you find the JarEntry object where "someResource.xml".equals(jarEntry.getName()).
I'm going to accept #Angelo's solution as it gave me the idea on how to work around this, though, I'd like to add more information in - thus my answer.
My work around was to add another constructor to SomeClass that takes in a java.net.URL instance. I also copied someResource.xml into the bundle's root.
I then updated the instantiation of SomeClass in the bundle like so:
new SomeClass( FileLocator.find( Activator.getDefault().getBundle(), new Path( "./someResource.xml" ), new HashMap< String, String >() ) );
This seems like a pretty big hack to me. What if I could not edit the contents of SomeClass ? I guess I would have to unpack it or I'd be forced to wrap it into it's own bundle?
I'm going to make the following assumption:
embedded.jar
src/SomeClass.class
someResource.xml
and the bundle contains embedded.jar and embedded.jar is on the Bundle-Classpath.
In this situation SomeClass.class.getResource("someResource.xml") will look for a resource on the classpath called src/someResource.xml because SomeClass is in the package src. In order to get someResource.xml in the root of the jar you need to do SomeClass.class.getResource("/someResource.xml") instead.
This isn't OSGi specific though, this is just how resource loading works in Java.

Setting CLASSPATH during runtime

How do I set a CLASSPATH variable during runtime while using IKVM?
I've been trying to do it by using:
java.lang.System.setProperty("java.class.path", "whatever");
The class I'm calling requires a configuration file in the classpath to work - and I keep getting errors that seem to indicate that it hasn't gotten its settings.
Is the way I'm trying to add variable incorrect?
If you really can't set the classpath beforehand yourself using the java's -cp or -classpath argument (why not by the way? that's the normal approach), then you can try to use URLClassLoader instead. Here's a kickoff example:
URL url = new URL(whateverPath);
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
ClassLoader urlCL = URLClassLoader.newInstance(new URL[] { url }, contextCL);
Thread.currentThread().setContextClassLoader(urlCL);
// ...
You only need to be lucky if the class you're calling is actually loading its resources through Thread.currentThread().getContextClassLoader().getResource() and thus not through
SomeClass.class.getClassLoader().getResource().
I was trying to do the same thing. I had some jar files compiled to a .Net dll but some of those (3rd party) jar files were trying to load their configuration files from the java classpath.
I solved the problem by specifying the -classloader option for the ikvmc tool. Example:
ikvmc -out:mydotnetapp.dll -classloader:ikvm.runtime.ClassPathAssemblyClassLoader c:/myjavaapp/lib/*.jar
This worked for me!
Source for the solution: http://old.nabble.com/Not-able-to-load-files-from-ClassPath-td31141788.html

Categories

Resources