I am trying to provide a plugin storage that every time at the beginning of the code load the plugins jars dynamically from the server . So i provided an interface and and all the plugins implemented this interface.
I am useing currently URLClassLoader and i created simple jar files to test my loader and it worked very well , but when i tested with the real jar files it seem that it didn't load the dependant libraries and gave me
java.lang.NoSuchMethodError: org.apache.http.impl.cookie.BrowserCompatSpecFactory.create(Lorg/apache/http/protocol/HttpContext;)Lorg/apache/http/cookie/CookieSpec;
this method is used in one of the methods of the jars .
Does URLClassloader loades also the required libraries ?
PS : The jar itself is tested from the terminal and its running well so the problem properly in the loader.
URLClassloader loads classes as and when required.
I suspect that in your case URLClassloader is loading incorrect version of dependent classes, pay attentio to NoSuchMethodError - it indicates that the class is loaded but it does not have a method definition.
Related
Recently I've been working on report generation with Jasper. I have created a simple program to test it and when running it via IDE it did work fine.
Then I moved the (very short) class to WildFly sever application and despite having the exact same code and library generation fails with cannot find symbol. Those symbols it cannot find are JREvaluator, JRFillVariable as well as packages such as net.sf.jasperreports.engine
In so far I have confirmed that:
Project builds (meaning those classes are visible for javac, but not jvm)
jasperreports-6.13.0.jar is added to war (it's present in /WEB-INF/lib folder alongside other libraries, like gson and hibernate
jasperreports-6.13.0.jar contains the missing classes
It looks to me like the problem doesn't lie in library not being loaded or missing classes (because in testing environment it works), but like something was preventing JBoss class loader from loading those classes
Attempted (and failed) solutions
Clean and Build
Adding -Djava.awt.headless=true to VM options - this did not changed anything
Adding -Djava.awt.headless=false to VM options - also didn't change a thing, but once caused NullPointerException inside jasperreport library. For testing program - worked in both cases
Adding commons-beanutils-1.9.4.jar, commons-digester-2.1.jar, commons-collections4-4.4.jar and commons-loggin-1.2.jar - with no changes
Adding jasper-compiler-jdt-5.5.23.jar - this caused a different error, namely NoSuchMethodError for org.eclipse.jdt.internal.compiler.ICompilerRequestor and few others. This library however should not be necessary as - from what I understand - jasperreport-6.13.0.jar already contains it's compiler and separate library for compiler is not required since a long time.
What has not been attempted:
Forcing the classes to load (http://www.java2s.com/Code/Java/Reflection/Forcethegivenclasstobeloadedfully.htm)
Dynamically loading jar during Runtime or using custom class loader
Update: after looking at this answer and applying the suggestion the missing class was different. Which suggests that the dependencies inside jasperreport.jar are not being loaded properly
I have figured it out
For some reason in server project libraries used by jasperreport.jar were not loaded, but in the testing project they were (might be due to WildFly, might be due to differences between IntelliJ and NetBeans)
Here is the list of libraries, based on pom.xml file in jasperreport.jar that I have added. Some might not be necessary and the list might not be exhaustive (I basically stopped adding libraries once report started generating) but it's good enough base if someone else runs into this problem:
commons-beanutils-1.9.4.jar
itext-2.1.7.jar
poi-ooxml-4.1.1.jar
commons-collections4-4.4.jar
jcommon-1.0.23.jar
xalan-2.7.2.jar
commons-digester-2.1.jar
jfreechart-1.0.19.jar
xmpcore-5.1.3.jar
commons-logging-1.2.jar
poi-4.1.1.jar
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.
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
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.
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!