I want to create a new ClassLoader everytime my method is called.
So I can reload a class without exiting my program.
A way how I can update a class loaded by ClassLoader would also be a solution.
How can I achieve that?
I found a good explained answer here:
http://www.exampledepot.com/egs/java.lang/reloadclass.html
The important thing is to have two binary folders, in my case:
one for the testcases and one for the program source.
Quote:
URL[] urls = null;
try {
// Convert the file object to a URL
File dir = new File(System.getProperty("user.dir")
+File.separator+"dir"+File.separator);
URL url = dir.toURL(); // file:/c:/almanac1.4/examples/
urls = new URL[]{url};
} catch (MalformedURLException e) {
}
try {
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class
Class cls = cl.loadClass("MyReloadableClassImpl");
saw this ? ClassLoader Load / Reload Example
I think this blog can satisfy your requirement.
Related
So I'm trying to make a plugin system like the "Bukkit" plugin system. See right now on my project I have some classes extending my base class "Plugin" and then I add them to my list. How would I make it so I can make it so it automatically loads jars from a "mods" folder that are extending my "Plugin" class and automatically add them to the arraylist? Thank you VERY much for the help, I'm trying to make a mod loader.
There might be better ways to do this, but this is what I have done in the past:
private static final String JAVA_CLASS_PATH_PROPERTY = "java.class.path";
private static final String CUSTOM_CLASS_PATH_PROPERTY = "custom.class.path";
public static void addPath(String s) throws Exception {
File f = new File(s);
URL u = f.toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", new Class[] { URL.class });
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[] { u });
if (System.getProperties().containsKey(CUSTOM_CLASS_PATH_PROPERTY)) {
StringBuilder sb = new StringBuilder();
sb.append(System.getProperty(CUSTOM_CLASS_PATH_PROPERTY));
sb.append(File.pathSeparatorChar);
sb.append(s);
System.setProperty(CUSTOM_CLASS_PATH_PROPERTY, sb.toString());
}
else {
StringBuilder sb = new StringBuilder();
sb.append(System.getProperty(JAVA_CLASS_PATH_PROPERTY));
sb.append(File.pathSeparatorChar);
sb.append(s);
System.setProperty(JAVA_CLASS_PATH_PROPERTY, sb.toString());
}
}
This adds the path to the jar (the s param) to the system class loaders list of URLS, then appends the path to the jar to the end of the custom class path if it exists, or the java class path otherwise. This should allow any other classes whose class loaders can reach the system class loader to use the newly loaded class.
You can load a class from a JAR but using a ClassLoader
URL jarUrl = ...;
URLClassLoader loader = new URLClassLoader(new URL[] { jarUrl });
Class myClass = Class.forName("myjar.mypackage.MyClass", true, loader);
MyPluginInterface myPlugin = myClass.asSubClass(MyPluginInterface.class).newInstance();
The myClass will be a class from the jar. Most likely you will want a JAR of interfaces you share with the plugin. By using these interfaces you can deal with instances which implement those interfaces easily. i.e. MyClass should implement MyPluginInterface which is an interface you provide.
Note: using a ClassLoader for each plugin allows you to unload the ClassLoader/JAR and load a new version of it, should it change.
I want to run the constructor of the Main.class in the package Test2, located in the folder C:\classes\
This is the code I'm using. It throws a class not found exception when it tries to turn it into a class. And then once it's part of the class object, will the constructor automatically be run, or do I have to instance it somehow? Test2 is inputted into this code as text.
if (Main.os.equals("Windows"))
{
String path = "C:\\classes\\";
}
else
{
String path = "~/classes/";
}
File file = new File(path);
try
{
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
Main.print("Stage 1");
ClassLoader cl = new URLClassLoader(urls);
Main.print("Stage 2");
Class cls = cl.loadClass(text + ".Main");
Main.print(text + " was loaded into memory.");
close();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
I suspect your problem is one of the following:
file doesn't exist or hasn't been properly specified. Check via file.exists()
Your class file is not located in the correct directory. If the package declaration for the Main class is package Test2; then your class file must be in the following location: C:\classes\Test2\Main.class.
If Main is nested class, then you will need to refer to the enclosing class when loading it, eg cl.loadClass("Test2.EnclosingClass$Main");
My guess it that your problem is number 2! :)
Good luck.
Oh, and yes, you'll need to create an instance of your object if you want the constructor to be called: clazz.newInstance() is the simplest method for no-args constructors.
Can you post the exact error message.
But here is how I execute a main method of using a class loader
urlLoader = new URLClassLoader(urls);
Class runClass = urlLoader.loadClass(classToRun);
System.out.println("Starting Program !!!");
Object[] arguments = new Object[]{args};
Method mainMethod = runClass.getMethod("main", new Class[] {args.getClass()});
mainMethod.invoke(null, arguments);
Note: classToRun will be the full package/class definition
i.e. net.sf.RecordEditor.edit.FullEditor
Note: I use it to load from jar files, it will be similar for directories
It is taken from the run class here
http://record-editor.svn.sourceforge.net/viewvc/record-editor/Source/RecordEditor/src/net/sf/RecordEditor/utils/Run.java?revision=65&view=markup
An example of calling the class is here
http://record-editor.svn.sourceforge.net/viewvc/record-editor/Source/RecordEditor/src/net/sf/RecordEditor/RunFullEditor.java?revision=65&view=markup
I'm trying to load dynamically a class contained in a .jar file. I know the whole class name and I know for sure that the class implements the interface AlgorithmClass.
My code looks like this:
addURLToSystemClassLoader(dir.toURI().toURL());
Class cl = Class.forName(algorithm.getClassName());
AlgorithmClass algorithmClass = (AlgorithmClass)cl.newInstance();
Where dir is the File object of the .jar file and addURLToSystemClassLoader(URL) looks like this:
private void addURLToSystemClassLoader(URL url) throws IntrospectionException {
URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> classLoaderClass = URLClassLoader.class;
try {
Method method = classLoaderClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(systemClassLoader, new Object[]{url});
} catch (Throwable t) {
t.printStackTrace();
throw new IntrospectionException("Error when adding url to system ClassLoader ");
}
}
I checked and the URL is being added to the class loader.
When I try to get the Class object I get the error:
SEVERE: javax.servlet.ServletException: java.lang.ClassNotFoundException: id3.Algorithm
(id3.Algorithm is the full name of the class I'm trying to load)
I've tried creating a new ClassLoader like below:
ClassLoader cload = new URLClassLoader(new URL[]{dir.toURI().toURL()}, ClassLoader.getSystemClassLoader());
Class cl = Class.forName(algorithm.getClassName(), false, cload);
AlgorithmClass algorithmClass = (AlgorithmClass)cl.newInstance();
But then I get the error:
java.lang.NoClassDefFoundError: lib/algorithm/AlgorithmClass
I've tried creating a new URLClassLoader with all the URLs that the system class loader has but the effect was the same.
The "worst" part of this is that both ways are working perfectly fine on the jUnit test that I have for testing this part of my code.
I'm using Glassfish 3.1.1 as my app server.
dir shouldn't contain 'lib'.
Try this:
ClassLoader cload = new URLClassLoader(new URL[]{dir.toURI().toURL()}, Thread.currentThread().getContextClassLoader());
Class cl = Class.forName(algorithm.getClassName(), true, cload);
AlgorithmClass algorithmClass = (AlgorithmClass)cl.newInstance();
You have class-loading issue. You shoud be aware that your addURLToSystemClassLoader() is actually the heck...
Put your jar to the classpath. Use Class.forName() idiom. If it fails use version that receives ClassLoader as parammeter namely
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
and path Thread.currentThread().getContextClassLoader() as ClassLoader parameter.
See also my another answer below.
I got some java-byte-code (so compiled java-source) which is generated in my program. Now I want to load this byte-code into the currently running Java-VM and run a specific function. I'm not sure how to accomplish this, I digged a little bit into the Java Classloaders but found no straight way.
I found a solution which takes a class-file on the harddisk, but the bytecode I got is in a Byte-Array and I dont want to write it down to the disk but use it directly instead.
Thanks!
you need to write a custom class loader that overloads the findClass method
public Class findClass(String name) {
byte[] b = ... // get the bytes from wherever they are generated
return defineClass(name, b, 0, b.length);
}
If the byte code is not on the classpath of the running program, you can use URLClassLoader. From http://www.exampledepot.com/egs/java.lang/LoadClass.html
// Create a File object on the root of the directory containing the class file
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
how can I dynamically load a class in Java with two parameters which are the absolute filepath of the class file and the name of the method I wish to call?
eg path: c:\foo.class
method: print()
I am just interested in the basics as a simple cmd line tool. A code example would b appreciated.
cheers hoax
Use URLClassLoader. The name of the method is irrelevant. You must pass the root directory of your package to the class loader. Then you can use the fully qualified class name (package + class name) in Class.forName() to get the Class instance. You can use the normal reflection calls to create an instance of this class and call methods on it.
To make your life more simple, have a look at commons-beanutils. It makes invoking methods much more simple.
Check out this example:
// Create a File object on the root of the directory containing the class file
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
After this, you could do something like this to first create a new instace using the default constructor and invoking the method "print" without arguments:
Object object = cls.newInstance();
cls.getMethod("print").invoke(object);