eclipse plugin classloading error - java

In a eclipse plugin project I have a jar file from which I want to load class and call methods. These jars added in bundle classpath in manifest file. How to load these classes runtime, if I try it with urlclassloader I get java.lang.NoClassDefFoundError but I am sure class is there. I read that eclipse osgi based and use its own classloader, and probably I get this error because of this. but why ? Do anybody have a good explanation and practical solution to my problem ?
Thank you.

This is a problem caused by OSGi's different class loading mechanism.
First of all, check if you added the right import-declarations to you manifest.
If the imports are right (and have the right version), they might not be visible because the JAR you are using is not exporting the needed classes.
OSGi can only see classes which are exported.
Using Eclipse, which means you are using Equinox as OSGi implementation, you might use Eclipse-Buddy-Classloading.
Create a new project with the JAR and export everything. Now register this as a buddy of your main bundle - the main bundle now can see every class of the providing bundle.
And keep in mind that using class loaders in OSGi is 'evil'. Classes can not be identified by their fully qualified class name anymore.
Take a look at the OSGi core specification for more information about OSGi class loading (osgi.org) and http://rajakannappan.blogspot.de/2010/03/eclipse-classloading-and-buddy-policy.html

I got a similar problem and here is my solution. It´s maybe a bit late but i hope it will help others with the same problem.
I noticed that the classloader from eclipse (osgi) doesen´t load the complete jar. It only loads the classes which are directly loaded via classloader.load(classname). So i just read all the classnames contained in the jar with the code from this question and iterate over all classNames by calling explicit classloader.load(classname). Here is a sample of the way i managed it:
private void loadClasses(final File jar) {
final URL url = new URL("jar", "", "file:" + jar.getAbsolutePath() + "!/");
final List<String> classNames = getAllClassNames(jar); //Code from above link in this method
final URLClassLoader classloader = new URLClassloader(url, this.getClass().getClassLoader());
for (String className : classNames) {
classLoader.load(className);
}
}
If you want to instantiate a class you can do it with
final Class<?> clazz = classLoader.load(className);
final T type = (T)clazz.newInstance();
Hope this helps.

Related

Java ClassLoader - NoClassDefFoundError from missing dependency

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.

What's the differences between the build function of Eclipse and javac?

I find a project in Eclipse has it own class path.
If i use maven create a new web project.
System.getProperty("java.class.path");
When i print the result,
I find the class path contains G:\newtool\workspace\springmvc\target\classes.
Who can explain the principles behind it.Why the class path is this directory.
Is the build function of Eclipse same as the command,javac -classpath?
And i find another question.
public class KnightMain {
public static void main(String[] args) throws Exception {
System.setProperty("java.class.path","G:/newtool/workspace;G:/newtool");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("knight.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
String s[] = System.getProperty("java.class.path").split(";");
for (String string : s) {
System.out.println(string);
}
context.close();
}
}
Although i set the class path to other directory where the knight.xml is not exist.But ClassPathXmlApplicationContext find it finally.Why the
System.setProperty("java.class.path","G:/newtool/workspace;G:/newtool");
makes no difference.Although the print result is:
G:/newtool/workspace G:/newtool
Two different tools, two different concepts.
First of all eclipse has its own java compiler. It is not using javac in any way. And the class path used by the eclipse compiler, is, surprise, what you told it to be.
In other words: when you create your eclipse projects, you configure its build path. The Build Path determines which libraries, other projects, ... should be imported, exported, ... and so on.
Whereas, when you open your command like shell, and you call javac directly, the classpath is based on the settings that you make there; for example by calling
javac -classpath /some/directory/with/classes:/some/bla.jar
Or in your case: you are using maven, which comes with its pre-defined rules, project dependencies, and so on. Thus your classpath there depends on the conventions/rules that maven applies for "java web projects". Thus: if you want to understand what maven is doing for you: you have to study the maven documentation for the features you are using there!
EDIT: I think what you are actually trying can't work. I think that this specific system property is more of a "read only" value. Meaning: you can use it to understand the current classpath. But writing the property will not change the classpath of the JVM you are currently running in.
Actually, that makes a lot of sense: if any piece of java code could change the classpath on the fly, that screams "security problem" all over the place! As that would allow you to completely change where the JVM would be loading its classes from.
The java.class.path property is used by JVM to locate classes and JAR files for loading. This system property is common for most available JVMs including Oracle and IBM implementations. This property is used by the default class loader, but not all custom class loaders. You can implement your own class loader, that ignores java.class.path and is using some other property to locate loader-specific packages. One example is Eclipse. Eclipse core has a custom class loader that is not using java.class.path.

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

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!

Make instance of class in java at runtime

In my program I generate classes dynamically but when I try:
String[] args = {"-d","D:\\path\\build\\classes","-s","D:\\path\\src","http://services.aonaware.com/DictService/DictService.asmx?WSDL"};
WsImport.doMain(args);
URL url = new URL("file:D:/path/build/classes/com/aonaware/services/webservices/");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
Class service = Class.forName("com.MyClass",true,urlClassLoader );
I recieve java.lang.ClassNotFoundException
If I run one more time the program (in Eclipse), then it is working. Actually I only have to refresh the project in Eclipse and then its working
Does anybody see the problem
Sounds like a classpath issue. Make sure that the class in question (or the jar containing it) is compiled and is in the classpath.
What exactly do you mean by "generating classes dynamically"? If you generate the java source file for a class, it needs to be compiled first into a class file, before the classloader can pick it up. Maybe Eclipse does that in the second round, that's why it is working then.
You would generally use a ClassLoader like URLClassLoader to load classes dynamically at runtime.
Use the three argument form of Class.forName() which requires you specify the ClassLoader.
[Java API Link][1]
[1]: http://java.sun.com/javase/6/docs/api/java/lang/Class.html#forName(java.lang.String, boolean, java.lang.ClassLoader)
Read the docs on URLClassLoader: Any URL that ends with a '/' is assumed to refer to a directory.
Also, you probably should use '/' as a separator instead of \\.
Addendum:
Well, your code works perfectly for me -- if there are actual compiled Java classes in the directory specified as an URL. But when you say
In my program I generate classes
dynamically
do you generate Java source or bytecode directly? If it's source code, do you also compile it from your app?

Categories