jar-in-jar class loading issue - java

i followed the steps described at Create cross platform Java SWT Application . i create a jar which has a swt_browser.jar containing only my class using swt library. then i added other platrom-specific swt jars.
i use the below code to load the swt_browser.jar and the platform specific swt library jar. but somehow the call for loading class SWTBrowser complains :
java.lang.ClassNotFoundException: org.eclipse.swt.widgets.Layout
can you tell what i am doing wrong?
-----code for loading swt jars-------
ClassLoader parent = Main.class.getClassLoader();
URL.setURLStreamHandlerFactory(new RsrcURLStreamHandlerFactory(parent));
URL swtBrowserFileUrl = new URL("rsrc:swt_browser.jar");
URL swtFileUrl = new URL("rsrc:" + swtFileName);
ClassLoader cl = new URLClassLoader(new URL[]{swtBrowserFileUrl, swtFileUrl}, parent);
Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
addUrlMethod.invoke(cl, swtBrowserFileUrl);
addUrlMethod.invoke(cl, swtFileUrl);
Thread.currentThread().setContextClassLoader(cl);
try {
// Check we can now load the SWT class -this check passes!
Class.forName("org.eclipse.swt.widgets.Layout", true, cl);
} catch (ClassNotFoundException exx) {
System.err.println("Launch failed: Failed to load SWT class from jar: " + swtFileName);
throw new RuntimeException(exx);
}
//this line below throws exception : java.lang.ClassNotFoundException: org.eclipse.swt.widgets.Layout
Class<?> c = Class.forName("com.sun.star.google.gui.SWTBrowser", true, cl);
Object obj = c.newInstance();
Method run = c.getMethod("run", url.getClass()); //$NON-NLS-1$
run.invoke(obj, new Object[]{url});

Your jar has the following file structure:
You are setting up the following ClassLoaders:
Main classloader
SWT classloader
The Main classloader has all of the following on its classpath:
Your app classes
The jar-in-jar classloader
The SWT classloader is constructed at runtime and has the following on its classpath:
The SWT classes for your platform
Your loading code asks the SWT classloader to load an SWT class and that works. However, you then ask it to load an application class. It doesn't know about your application class so it delegates to its parent, the Main classloader. This manages to load your application class which then tries to reference an SWT class. This reference is handled by the classloader which loaded your application class - the Main classloader. This doesn't know about the SWT class and throws an exception.
You need to package your application differently. You need to have the following classloaders.
Main classloader
SWT classloader
The Main classloader has all of the following on its classpath:
A single app class which handles building the SWT classloader
The jar-in-jar classloader
The SWT classloader is constructed at runtime and has the following on its classpath:
The SWT classes for your platform
Your app classes
This means that when you load your app class it will be loaded by the SWT classloader. This means that when your app refers to SWT classes the correct classloader is used.
For a working example of this, you can download and examine the following jar: https://github.com/downloads/mchr3k/org.intrace/intrace-ui.jar
EDIT: The ant build file which produced intrace-ui.jar can be seen here: https://github.com/mchr3k/org.intrace/blob/master/org.intrace/build.xml
In particular, the "jar" target handles the packaging.

Related

Externalize classes in multiple jars

So i've been working on a project of mine which works in modules. A module is simply an abstract class which all the modules extend from and replace the empty methods they need to. No modules have public functions that arent defined in the abstract class and each function is called depending on specific events.
What i'd like to do is externalize each module (for this exemple ill refer to Autorole as an exemple module) into its own jar that the main class will fetch and use. That would allow for Autorole to be hotswapped/hot updated by simply replacing it's old jar file with the new one and reload the main program (rescan the jars in the modules folder and load them, without turning off the main program)
Edit1 : Very much like craftBukkit loads it's plugins
Currently my approach is to export each module as an executable jar and start it with a command using the runtime environment of the main program. (java -jar Autorole.jar for eg)
The main function of each module simply adds an instance of their module (here Autorole extended from the module class) into a static list in the main class of the main jar. And it works fine, its able to execute the Autorole jar properly and use it like it used to when everything was in the same jar.
But here comes the problem.
Autorole depends on the main class and thus has it as a dependency, and i dont know how to make it able to recognize the main file as their dependency, meaning right now i need to have a copy of the main class for each module which is far from convenient or optimized.
What i wanted to try at first was specify the path the main file is in but i got stuck rather quickly. The path cant be hardcoded since the main file has versions and modules arent version specific, plus the name might not be the exact same as expected, so i thought of passing the path of the main jar as a java arg, and that's where i'm at as i dont know what to do once i have the string in hands
tl-dr:
How can i load a jar from another like craftBukkit loads it's plugins
The Class Loaders allow you to dynamically load classes into a JVM process. URLClassLoader is a class loader that loads classes from a given URL e.g. external jar on the file system.
Just call ClassLoader#loadClass(String name) with the fully qualified class name, e.g: Class<*> theClass = classLoader.loadClass("com.packagename.ClassName"); and you will receive a Class. Then you'll have to get a constructor which you want to use to create this class's instance, e.g. Constructor<*> theConstructor = theClass.getConstructor(String.class); Finally invoke the constructor using Constructor#newInstance(Object... initargs) providing arguments required by the constructor.
Most likely, you will also want to set your plugin's class loader as a parent class loader.
Full example:
URL[] jars = new URL[1];
jars[0] = new File("path/to/file.jar").toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(jars, YourPlugin.java.getClassLoader());
Class<*> loadedClass = classLoader.loadClass("com.example.ClassName");
Object classInstance = loadedClass.getConstructor().newInstance();

DocumentBuilder.class operator loads the class in System Classloader

We are using Child first classloader to avoid jar conflicts and load the classes from a particular path. With the service loader and the xml-apis jar in the child classloader's classpath, we are facing issues.
xerces2 jars service loader creates an object of javax.xml.parsers.DocumentBuilderFactory with org.apache.xerces.jaxp.DocumentBuilderFactoryImpl if DocumentBuilderFactoryImpl not present in the classpath, It loads the fallback class present in the rt.jar. Under normal circumstances, the code works fine.
With using child first classloader on top of System classloader, we facing issues in the following lines.
DocumentBuilderFactory newInstance()
the above line is used for creating the object. The method details are,
public static DocumentBuilderFactory newInstance() {
return (DocumentBuilderFactory)FactoryFinder.find(DocumentBuilderFactory.class, "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
}
The first argument to the find method,
DocumentBuilderFactory.class
creates a class object for javax.xml.parsers.DocumentBuilderFactory
class. but even we have set the classloader with child classloader,
.class operator loads the class with System classloader and creates a
class object, not with the current child classloader on top
.
Inside the service loader, creates an object for org.apache.xerces.jaxp.DocumentBuilderFactoryImpl with child classloader and the child first classloader loads the javax.xml.parsers.DocumentBuilderFactory class.
As the DocumentBuilderFactory.class loaded in the system classloader and the org.apache.xerces.jaxp.DocumentBuilderFactoryImpl class loaded in the top child classloaders are not assignable to each other as both javax.xml.parsers.DocumentBuilderFactory is created in separate classloaders.
My question is,
Is there any way to avoid the DocumentBuilder.class from loading to the system classloader?
For child first classloader we are using the following library
https://github.com/kamranzafar/JCL/

Loading external Classes with external dependencies - URLClassLoader ClassNotFoundException

I am loading a Class from an external.JAR file and by means of URLClassLoader, which works as long as the external Class does not reference another JAR. If I do it yields a ClassNotFoundException.
As a workaround I add the other second tier JAR as a dependency, but I would like to load these also dynamically on runtime.
Question: How do I load an external Class which references other external classes? or how do I load external JAR files and classes, in
the correct order so I am not getting an exception?
Should I catch the exception and then "first" load that class that was not yet loaded?
You actually could load them all using a child first class loader. That main jar and all of its dependencies would be apart of that class loader and can be referenced and thrown away if needed.
Main Class Loader -> ChildFirstClassLoader -> Loads jar and dependent jars
This is a good example
Here is another SO similar reference
Semi Example
File file = new File("c:\\free-universe-games-llc\\app.jar");
URL url = file.toURI().toURL();
ChildFirstClassLoader urlClassLoader = new ChildFirstClassLoader(new URL[]{url}, SomeClassLoader.class.getClassLoader());
Class<?> aClass = urlClassLoader.loadClass("com.freeuniversegames.app.App.class");
Object o = aClass.newInstance();

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 .

Java Classloader unable to load modules?

I seem to be having a problem with loading classes in a module loader for an application I'm developing. Basically, all classes I'm going to be loading with it extend another class, which is located in a package in the actual application. For our purposes, we'll call it Module. Modules are located in a separate folder outside the actual application.
The loader iterates through a folder and executes the loadFile() method on any file with the extension .class. All classes have the package declaration as the Module class, as well as the extends Module declaration in the class header.
This is the loadFile() method, header and exception clauses excluded:
String fileName = file.getName();
String className = fileName.replace(".class", ""); //Strips extension
Class<?> aClass = Class.forName(className, true, new URLClassLoader(new URL[] { file.toURI().toURL() }));
Class<? extends Module> modClass = aClass.asSubclass(Module.class);
return modClass.getConstructor().newInstance();
I keep getting a ClassNotFoundException on the third line. And past that, if it ClassNotFoundException weren't thown, would all dependencies be resolved?
From the documentation for URLCLassLoader:
This class loader is used to load classes and resources from a search path of URLs referring to
both JAR files and directories. Any URL that ends with a '/' is assumed to refer to a directory.
Otherwise, the URL is assumed to refer to a JAR file which will be opened as needed.
So, you must use URLs for either directories or .jars
Two solutions:
Force your users to give you .jar files, including a manifest of some sort inside with the classname that they wish to be loaded. This approach is used by the Bukkit developers. Having used this method in the past, the dependencies should all be packaged in the .jar file and thus in the URLClassLoader's search path and able to be loaded.
Use the URL of the file's directory, and search that directory for .class files. I'm not sure if dependencies will be loaded using this method.
In the URLClassLoader, do not pass the file, but the parent folder. However, this works correctly if classes are all in the "default package", so the .class files you are loading must not have a package declaration on top.
By default, a class loader will also trigger loading of all classes required to properly build the class: it will try to load the super class, the super super class etc... all the interfaces and super interfaces, the classes needed for static fields and methods, the classes needed for method signatures (return types and parameters). It will not usually try to load classes used internally by methods, not until you execute those methods.
However, usually a class loader does not "contain" all those classes, for example your class will end up inheriting from java.lang.Object, and your URLClassLoader will not contain the Object.class file. So, class loaders delegate to their parent class loaders.
You are currently creating a URLClassLoader without specifying a parent, in Java 7 at least the parent will default to the "system class loader", which is fine as long as you are in a plain java application, and not executing your code itself inside a specific hierarchy of class loaders. If however you are running that code in a web application, or in an OSGI container etc.. the you should give the URLClassLoader a proper parent to delegate to, for example Thread.currentThread().getContextClassLoader() or this.getClass().getClassLoader().
I suppose you need all of this because you need to load those class dynamically at runtime.

Categories

Resources