Java ClassLoader - NoClassDefFoundError from missing dependency - java

I'm trying to load a class at runtime using the classloader. The code looks roughly so:
URLClassLoader jarClassLoader = new URLClassLoader(new URL[] { myIFile.getLocationURI().toURL() }); //myFile is a jar packaging required librairies
Class<? extends RTTestCase> testCaseClass = (Class<? extends RTTestCase>)jarClassLoader.loadClass("com.myPackagePath." + testCaseClassName);
RTTestCase myTestCase = testCaseClass.newInstance(); //java.lang.NoClassDefFoundError: org/eclipse/core/resources/IProject
jarClassLoader.close();
So it seems the IProject class is not found by jarClassLoader. I did export my class as jar with the "export libraries" option in Eclipse, so the JAR containing this class (some eclipse library) is available in the exported JAR as well as in the system path.
So what am I missing here? Is it a setup problem where the exported JAR is not properly packed, or do I need to load IProjects and all other dependencies manually first? How to do that ?
Background:
I am building an application which is started from an Eclipse environment. This application finds other projects from the current workspace and from each project executes a big program that might crash in difficult-to-analyze ways if the project content is not correct or if the big program itself has bugs.
Before executing the big program I want to copy and modify the Eclipse project in various ways, to "transform" it. So I want to have the possibility to separate these transformation rules from my application itself, by letting my colleagues simply implement classes inheriting from RTTestCase, code whatever they want to do on the project, pack it in a jar and then the application finds it, load the main class from the jar (standardized naming) and can execute the transformation by passing the current project to the class and running a main function.
Other solutions, such as starting the jar from a command line are not optimal, since the project would probably have to be loaded again and we're talking about gigabyte-big projets (and numerous executions). Furthermore loading the transformation rule from the application would allow the transformer to provide a configuration GUI for example, or a evaluation hook called again directly from the current execution thread.

I think I have a solution - partially at least... By instantiating the ClassLoader this way:
URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { testCaseFile.getLocationURI().toURL() }, Thread.currentThread().getContextClassLoader());
Which gives the current thread ClassLoader (i.e. my application's ClassLoader) as parent of the new ClassLoader. So it seems that when the loaded class tries to use a class, it is first searched in the parent ClassLoader, which has access to the entire Eclipse environment (since my application is started from Eclipse).
I'm saying "partially" because I might have problems if I try to execute the application outside of Eclipse, although it should be possible to pass the eclipse classpaths as argument. Also I have only performed minimal testing (hello wolrd).
Update:
It worked... until I decided to instantiate new class from my loaded class, for example:
Class<? extends ParentClass> myClassA = urlClassLoader.loadClass(pathA);
Class<? extends ParentClass> myClassB = urlClassLoader.loadClass(pathB);
ParentClass myA = myClassA.newInstance(); //OK
ParentClass myB = myClassB.newInstance(); //OK
myA.methodThatInstantiatesClassB(); // NOK -> ClassDefNotFoundException
I guess the main thread Classloader is not inherited further by the dynamically loaded and instantiated classes... So I started searching about how to pass the ClassLoader, or change the system ClassLoader, before I eventually notice that since I'm starting my tool from Eclipse, I can set all my additional JARs as libraries for the project from which I start my tool, so that the JARs are in the system ClassLoader (I still need to find the < ? extends ParentClass> dynamically, but then I don't have to worry about their dependencies, as everything is in their JARs).
I do this statically (configure build path in project), but I suppose it would also be possible to search the JARs in a folder and add them to the System Classpath at start (ANT script for example).
In short:
Add JARS to classpath when starting the program, either over Eclispse configuration or via ANT/command line
Dynamically load the "main" class (which extends an abstract parent class known by the program) and use them normally after that.

Related

How to load My Jar to ClassPath at runtime through Java Coding?

I am working with Swing in Net Beans. I have my own jar which contains classes and methods inside it. I will call those classes and methods using JAVA Reflection API but before that I want to load my Jar into class path at run time. I have a J Button and on click of that I am getting Jar Name and Jar path. But I am failing to load Jar to classpath at run time. Got some links but were not helpful. Please provide me with simple example. I should load my jar to classpath. That's the only problem for me.I will take care of that. Please help.
You can load classes at run time through the use of a ClassLoader, take a look at URLClassLoader for example
File yourJarFile = ...;
URLClassLoader classLoader = new URLClassLoader(new URL[]{yourJarFile.toURI().toURL()});
This will then allow you to load classes and instantiate them...
Class class = classLoader.loadClass("fully.qualified.packagename.to.your.AwesomeClass");
You can then instantiate them using something like...
Object obj = class.newInstance();
Or reflection if you want to use a specific constructor. Just remember, you won't be able to reference these classes directly within the current class loader context, as the current class loader knows nothing about them

Class loading in eclipse plugin

I made a eclipse plugin that acts on JavaProject. It needs access to information contained in the bytecode of the classes of the project and, therefore, I used a URLClassLoader (saying to it that the classes are in the "bin" folder of the project) to get the reference to classes and retrieve all the information I need . Unfortunately, when I call the method loadClass("a certain class in JavaProject") I get an error of this type:
org.eclipse.e4.core.di.InjectionException: java.lang.NoClassDefFoundError: javassist/bytecode/BadBytecode
I have found that errors of this kind are due to the fact that external libraries added to the JavaProject's BuildPath are not "known" by the classloader: classes of these libraries are used by JavaProject's classes
In the previous case was used the BadBytecode class of library javassist
in this statement of a class of JavaProject
public static void main(String[] args) throws NotFoundException, BadBytecode, IOException, CannotCompileException{
So how do I make my plugin visible to the classes of external libraries imported into the java project?
You can access Java project's build path by using JavaCore.create( [IProject] ) API which gives you an IJavaProject that has API to traverse the build path. Having said that, you should definitely not be doing this. URLClassLoader has no notion of reloading existing classes, so it would never see updated versions as user edits their code and it has a tendency of locking jar files (such as ones on your build path). JDT has API for safely traversing type structure of the Java Project that doesn't involve using class loaders.
You must get the classloader in the selected java project, then use the classloader to load class. Java project's classloader is different from the classloader in eclipse plugin.
see the detail code in the following link:
https://sdqweb.ipd.kit.edu/wiki/JDT_Tutorial:_Class_Loading_in_a_running_plugin

UnsatisfiedLinkError: The library loads but I still get a link error

I have two Eclipse plugins:
plugin-1: provides a package in .jar to other plugins. (a Java Wrapper for a C++ library) This plugin was created by clicking File->New->Other->Plug-in from Existing JAR Archives.
plugin-2: has the native library .so for plugin-1 (Bundle-NativeCode directive is in MANIFEST.MF) and instantiates a class from plugin-1
(I actually tried putting the .so in plugin-1, but I cannot seem to load the library, even with the Bundle-NativeCode directive in the plugin-1 MANIFEST.MF, outside of the plugin project that contains the .so, so I guess I have to bundle the .so with any plugin that uses plugin-1.)
I am running a JUnit tests from plugin-2 which instantiates MyClass from plugin-2 which, in turn, instantiates MyLibraryClass from plugin-1. MyClass successfully loads the native library and instantiates MyLibraryClass without an UnsatisfiedLinkError or other exception being thrown from either the loading of the native library or from instantiating MyLibraryClass. I am not running a plugin in this case -- just the JUnit tests.
When I run plugin-2 (using a product configuration) and instantiate MyClass, the native library loads fine but I get an UnsatisifiedLinkError when MyClass instantiates MyLibraryClass. In this case, I believe the library is being loaded based on the output I get from using the class described in the posting How do I get a list of JNI libraries which are loaded?
NOTE: I'm using Eclipse 3.6.1.
Here is a code sample that shows the essence of what I'm trying to do:
package com.mylibrary;
import com.external_library.MyLibraryClass;
public class MyClass {
public static void loadLibrary() {
// Without Bundle-NativeCode in MANIFEST.MF I get
// "java.lang.UnsatisfiedLinkError: no mylibrary_java in java.library.path"
System.loadLibrary("mylibrary_java"); // Loads libmylibrary_java.so.
// Works fine from JUnit Test
// When I run the plugin, I get an UnsatisfiedLinkError:
// "java.lang.UnsatisfiedLinkError:
// com.external_library.MyLibrary_javaJNI.new_MyLibraryClass__SWIG_3()J"
MyLibraryClass instance = new MyLibraryClass();
}
}
I have replicated your setup and I get the same exception.
The problem could be solved by:
add the native library to plugin-1
add the Bundle-NativeCode directive to plugin-1's Manifest
load the library in the static constructor of plugins-1's Activator (you can write one and add it to the plugin)
Some other possible sources of errors:
Be aware that the package path, the class name and the method signatures should never be changed for any class with native bindings. Otherwise JNI would not be able to find the native counterpart and you get an UnsatisfiedLinkError. In your import directive you specified the following classname com.external_library.MyLibraryClass, but your error message has a different classname com.external_library.MyLibrary_javaJNI. Check for these sources of errors.
Some additional explanations:
A JUnit test in contrast to an JUnit plugin test starts no OSGi environment. Therefore you have a plain Java application with an ordinary JUnit test. If your native lib and your application are contained in the same folder (top level) the native lib will be automatically found on windows. If that is also true on UNIX systems, this would be an explanation why your JUnit test is successful. If it lies in a different folder, you have to specify the Java Library Path for an ordinary Java application.
EDIT by MrMas:
Modify plugin-2 so it doesn't depend on plugin-1 by adding the .jar file to plugin-2.
Copy the .jar file into plugin-2. I put it in the same directory as the .so.
Add the jar to the project via: Project->Properties->Libraries->Add Jar
Add the jar to the class path via plugin.xml->Runtime->ClassPath section->Add
Export the packages from the Jar (if they're needed by downstream plugins)
Remove the dependence of plugin-1 from the plugin.xml->dependencies tab
Now you can load the library with a System.loadLibrary and use the classes from within the plugin and from another plugin.
I chose not to modify plugin-1 because it was created as a plugin from an existing jar to which I couldn't discover how to add an Activator. I instead chose the path of adding the .jar to plugin-2. See Adding jars to a Eclipse PlugIn for additional discussion.
Bundle-NativeCode is an OSGI-tag. This means only OSGI classloaders are using it. In my case, I had an E4-RCP application. One plugin contained the Java class. The native code, however, I put into a fragment.
When loading and looking for a library, the OSGI classloader has a list of fragments (according to the naming of the structure involved) and examines their Bundle-NativeCode using the class NativeCodeFinder. If one has troubles, try to add breakpoints at the relevant functions. getNativePath() returns the entries as read by the OSGIpart.

dynamically loading classes from a ImageJ plugin

I am having problems dynamically loading a java class from a dynamically loaded class.
My class is an ImageJ plugin and it loads other modules dynamically through classloader. I have NoClassDefFoundError when loading the module that references to something that is in the same package as the ImageJ plugin.
What I exactly do is:
ImageJ loads plugin (A)
(A) gets system class loader
(A) add jar url to class loader
(A) try to load the desired class (B) in the jar using Class.forName
I am unable to load class B because I get a NoClassDefFoundError caused by B pointing to a class in A that was not found in the current classloader.
I think I need to use the same classloader ImageJ used for loading plugins on the first place to be able to load my modules and still find references to jars previously loaded by ImageJ. I would like to do this without having to recompile ImageJ.
Is there any way to fix this problem without having to indagate ImageJ code or having to modify it?
pseudo classes example
package a;
class A extends PlugInFrame {}
package a;
class C extends MyOwnPlugIn {}
package b;
import a;
class B extends C {}
The easiest way is to refrain from loading the class dynamically, but directly, by using it.
Absent that method, the best way is to make sure that the .jar files are all in ImageJ's plugins/ folder (or if you use Fiji, non-plugin .jar files live in jars/) and use ImageJ's plugin classloader: IJ.getClassLoader().loadClass("blub");
Note that your step 3 is a big no-no: you should never add elements to the system class loader's classpath.
Note also that all the ImageJ experts can be reached via imagej#list.nih.gov, not by posting on StackOverflow.
I'm not sure whether you need to be loading these classes dynamically but to get A's classloader
ClassLoader classLoader = A.getClass().getClassLoader()
Then use that to instatiate B bearing in mind that B needs to have a no argument constructor.
Why don't you build you build your plugin jar file incorporating B? I've created a bunch of plugins andgenerally I add dependancies and then build the plugin jar file incorporating all dependencies that it needs.

executing java method with dependencies inside a dynamically loaded class

I have my jar file with its own libs like any other netbeans project. Additionally I have a "plugins" folder to store plugins in a .jar file format. I´m loading the plugins using an URLClassLoader and I´m also executing the proper method without any problem.
However, the jar file in the plugins folder may contain dependencies with other packages (java-mail, for example) who aren´t in my classpath (the plugins folder isn´t in my classpath neither), so I´m worried whether that plugin would be executed correctly. I also don´t know where I should store that dependency.
Could I do something to overcome this issue? Where should I store the plugin dependency?
Thanks.
The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
(From the javadoc of ClassLoader)
The paragraph above means that normally, your class loaders form a tree and each one asks its parent before trying to load a class. So in theory, if your dependencies are visible by either the class loader that is loading the plugin, or any of the upstream class loaders, it will work.
However, there are two caveats with this:
If your plugin calls Thread.setContextClassLoader(), that might mess things up. But this should be more of the plugin writers' concern rather than yours.
Web servers typically don't obey this delegation rule to ensure maximum separation between different webapps and the server itself.
(I could probably give a less generic answer if there was some code I could look at.)
According to biziclop, i have put the dependencies inside the plugin folder. It seems to work, but i have needed to download extra packages.
To clarify, i made a plugin using the apache httpClient library. I made the jar file, and then i put the jar file with its dependencies (httpClient and httpCore) inside the plugin folder. When i executed the plugin a "NoClassDefFoundError" appeared. To overcome this issue i have download the "commons-logging" and "servlet" packages and i have added them into the plugin folder.
At least, that´s not my problem because the plugin developer should test the plugin and provide those extra packages, but the fact is that those extra packages are unveiled while testing the plugin and not while compiling it.
Thanks for the help!

Categories