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
Related
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.
So, I am trying to do something ugly here, let's say it's a desperate measure as I cannot have control over the runtime environment.
So having that said, I run some code in an environment where I cannot have control over the classpath (bad thing)... What's still worse is that the classpath has two jars, let's say productV1.jar and productV2.jar Both are exactly the same, but in different versions, so they have same classes.
For some reason, in most environments the productV2.jar is loaded and the productV1.jar is left out, but in some other environment, productV1.jar is called, and it causes the program to crash.
What I want to do as a workaround is mess with the classloader to explicitly ignore "productV1.jar". Ideally by overwriting some classloader funtion. I have done this with other resources (like the persistence.xml file from Hibernate), but I don't want to filter based on containing classes, but rather on the jar file. Is there a way?
This is only meant to work until I can do the negotiations to get rid of the offending jar...
Edit: I will leave this question open in case there is some interesting hack. However, the issue is that everything that is in the classpth is loaded by the system classloader, and trying to switch it at runtime is not an easy task (or maybe not possible at all?). Only way I see is starting the process with a custom classloader, which is not what I am looking for.
Did you try to use URLClassLoader and manually add jars to the classpath? In constructor of URLClassLoader you pass an array of URLs which point to jars that you want to load.
You can make the following experiment: create an URLClassLoader subclass that includes only the jars you want (i.e. call super constructor with appropriate array of URLs) and then call your java with the following property:
-Djava.system.class.loader=test.CustomJarsClassloader
To set your classloader as the default one.
The classloader may look like:
public class CustomJarsClassLoader extends URLClassLoader{
public CustomJarsClassLoader(){
super(new URL[]{ /*List of URLs to jars... */});
}
}
UPDATE:
Ok, if you can't add this argument to command line then try another approach:
In your main() function create a new Thread
Set the classloader that I mentioned above as this thread's context classloader (see: javadocs)
Run all your application's code inside this Thread. Your classloader should be used to load classes.
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.
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.
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?