Reload class using custom class loader - java

i am planning for the hot deployment of class using the custom class loader.For this task i have written custom class loader. Which looks like
public class CustomClassLoader extends ClassLoader{
public CustomClassLoader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String name) throws ClassNotFoundException {
if(!"classLoader.TestCase".equals(name))
return super.loadClass(name);
try {
String url = null;
String clzName = null;
url = "file:/home/naveen/workspace/JavaConcept/bin/classLoader/TestCase.class";
clzName = "classLoader.TestCase";
}
URL myUrl = new URL(url);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass(clzName,classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
Now i have a another class called TestCase which has an method called print().
public void print(){
System.out.println("Hello");
System.out.println("Bye");
}
and i am calling it from main method something like this
public static void main(String arg[]) throws InstantiationException, IllegalAccessException{
TestCase t = new TestCase();
t.print();
try {
ClassLoader classLoader = CustomClassLoader.class.getClassLoader();
classLoader = new CustomClassLoader(classLoader);
Class clz = classLoader.loadClass("classLoader.TestCase");
TestCase t2 = new TestCase();
t2.print();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Now here i want to do one thing that inside a main t.print() method is called which print hello and bye on console. Now i have a another version of this Testcase class's print method which only print Hello. So what i did i stated the program in debug mode and let the program going on untill the classLoader.loadClass() line. then i replaced the Testcase.class from directory structure with new version which print only hello.But still it showing the output Hello and Bye.
Can someone help me what's wrong with this program or my understand regarding the class loader is not correct. Please correct me and help to complete my task.

Custom class loaders are difficult to implement because of the many rules you need to keep in mind. In your case, I guess the problem is that your class file is part of your program, which means that if you try to load a class, the default class loader will load it before calling your loadClass() method that will never be called. Try removing your class file from your classpath so that its not part of your program, then your loadClass() should be called.
Second problem is that you are creating your TestCase object using new, that will invoke the default class loader, a better approach would be to call classLoader.loadClass() and then create an instance from the class returned, but if you cast to TestCase that it means that the class is defined in your default classloader which leads to the first problem. An alternative is to create an interface and cast to that interface in order to call your method.

Related

Class Not Found Exception while using class Class

Plz check following code.... class testError has been instantiated but still Class not found exception is generated... If that is true then why statement written in exception handler does not get printed??
class testError
{
void display()
{
System.out.println("This is testError Class");
}
}
class checkResult
{
public static void main(String[] args)
{
testError te = new testError();
te.display();// I hope the class has been created
Class cls = Class.forName("testError"); // will throw ClassNotFound exception
// Why??... Though the class has been
// instantiated
// if we try to put it in trycatch block it will work...Why??
try{ Class cls = Class.forName("testError");}
catch(ClassNotFoundException e)
{
System.out.println("Error found"); //"Error found" will not be printed
// as the class has been instantiated
}
}
}
I can't comment - as my reputation is too low, but your code runs and debugs fine - though I had to alter it a little bit to make it compile:
public static void main(String[] args) throws ClassNotFoundException {
testError te = new testError();
Class<?> cls = Class.forName("testError");
try {
cls = Class.forName("testError");
// If you got there then everything went fine
te.display();
}
catch (ClassNotFoundException e) {
System.out.println("Error found");
}
}
How do you run your code (from the command line, in an IDE)? What is the console output?
You have got to give more helpful information if you want people to investigate your issue.
Finally, Java convention specifies that classes name should begin with an uppercase character (CheckResult and TestError). Also you should avoid using classes in the default package, as those cannot be imported.
First of all Follow java naming convention
Make your main class public
Create some package(not good to create in default packag) like mypackage and put the classes inside them
and try to invoke the method this way
String name = packageName.className.class.getName();//get the name of the class
className o = (className)Class.forName(name)
.newInstance();
//will give an instance of type Object so cast it
o.display();// call the method

Reload used classes at runtime Java

I'm working on a program that watches a directory and runs all tests in the directory when it sees changes in the directory.
This requires the program to dynamically load the classes, instead of getting the cached copies.
I can dynamically load the test classes. Changes to the tests get detected and used at runtime. However, this isn't the case for the classes tested by the tests.
My code for dynamically loading the classes and returning a list of test classes:
List<Class<?>> classes = new ArrayList<Class<?>>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\\', '.');
name = name.substring(0, name.length() - 6);
tempClass = new DynamicClassLoader(Thread.currentThread().getContextClassLoader()).findClass(name) } catch (ClassNotFoundException e1) {
// TODO Decide how to handle exception
e1.printStackTrace();
}
boolean cHasTestMethods = false;
for(Method method: tempClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
cHasTestMethods = true;
break;
}
}
if (!Modifier.isAbstract(cachedClass.getModifiers()) && cHasTestMethods) {
classes.add(tempClass);
}
}
return classes;
with DynamicClassLoader being as the Reloader described here How to force Java to reload class upon instantiation?
Any idea how to fix it? I thought all classes would be dynamically loaded. Note however that I don't overwrite loadclass in my DynamicClassLoader because if I do my test classes give init
EDIT:
This doesn't work, the class gets loaded but the tests in it aren't detected...
List<Request> requests = new ArrayList<Request>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\\', '.');
name = name.substring(0, name.length() - 6);
Class<?> cachedClass = null;
Class<?> dynamicClass = null;
try {
cachedClass = Class.forName(name);
URL[] urls={ cachedClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = cachedClass .getClassLoader().getParent();
URLClassLoader cl = new URLClassLoader(urls, delegateParent) ;
dynamicClass = cl.loadClass(name);
System.out.println(dynamicClass);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Edit edit: i detect the test methods like this:
for(Method method: dynamicClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
requests.add(Request.method(dynamicClass, method.getName()));
}
}
If you used the custom ClassLoader exactly like in the linked answer it is not overriding the method protected Class<?> loadClass(String name, boolean resolve). This implies that when the JVM is resolving dependencies it will still delegate to the parent class loader. And, of course, when it was not delegating to the parent ClassLoader it had the risk of missing some required classes.
The easiest solution is to set up the right parent class loader. You are currently passing Thread.currentThread().getContextClassLoader() which is a bit strange as your main intention is that the delegation should not delegate to that loader but load the changed classes. You have to think about which class loaders exist and which to use and which not. E.g. if the class Foo is within the scope of your current code but you want to (re)load it with the new ClassLoader, Foo.class.getClassLoader().getParent() would be the right delegate parent for the new ClassLoader. Note that it might be null but this doesn’t matter as in this case it would use the bootstrap loader which is the correct parent then.
Note that when you set up the right parent ClassLoader matching your intentions you don’t need that custom ClassLoader anymore. The default implementation (see URLClassLoader) already does the right thing. And with current Java versions it is Closeable making it even more suitable for dynamic loading scenarios.
Here is a simple example of a class reloading:
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
public class ReloadMyClass
{
public static void main(String[] args)
throws ClassNotFoundException, IOException {
Class<?> myClass=ReloadMyClass.class;
System.out.printf("my class is Class#%x%n", myClass.hashCode());
System.out.println("reloading");
URL[] urls={ myClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = myClass.getClassLoader().getParent();
try(URLClassLoader cl=new URLClassLoader(urls, delegateParent)) {
Class<?> reloaded=cl.loadClass(myClass.getName());
System.out.printf("reloaded my class: Class#%x%n", reloaded.hashCode());
System.out.println("Different classes: "+(myClass!=reloaded));
}
}
}

Why isn't my class being loaded through the external class loader?

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

read from class defined by variable

I have Java-related question:
I want to know is there a way to create path to class (in program) by using a variable(s).
Im making a program that will download pictures from certain sites and show them to a user. However, different sites have different forms, that's why I have to define a series of functions specific to each. They cannot be put in the same class because functions that preform same job (just for another site) would have to have same names. I'm trying to make adding support for another site later as simple as possible.
Anyway, the question is, could I call a function in program using a variable to determine its location.
For example: code.picturesite.functionINeed();
code is the package containing all of the coding, and picturesite is not a class but rather a variable containing the name of the desired class - that way I can only change value of the variable to call a different function (or the same function in a different class).
I don't really expect that to be possible (this was more for you to understand the nature of the problem), but is there another way to do what I'm trying to achieve here?
Yes, there is a way. It's called reflection.
Given a String containing the class name, you can get an instance like this:
Class<?> c = Class.forName("com.foo.SomeClass");
Object o = c.newInstance(); // assuming there's a default constructor
If there isn't a default constructor, you can get a reference to one via c.getConstructor(param1.getClass(), param2.getClass(), etc)
Given a String containing the method name and an instance, you can invoke that method like this:
Method m = o.getClass().getMethod("someMethod", param1.getClass(), param2.getClass(), etc);
Object result = m.invoke(o, param1, param2, etc);
I'm not immediately seeing anything in your question that couldn't be solved by, instead of having a variable containing a class name, having a variable containing an instance of that class -- to call a function on the class, you would have to know it implements that function, so you could put the function in an interface.
interface SiteThatCanFoo {
void foo();
}
And
class SiteA extends Site implements SiteThatCanFoo {
public void foo() {
System.out.println("Foo");
}
}
Then:
Site currentSite = getCurrentSite(); // or getSiteObjectForName(siteName), or similar
if (SiteThatCanFoo.isAssignableFrom(currentSite.class)) {
((SiteThatCanFoo)currentSite).foo();
}
So you want to do something like this (check ImageDownloader.getImageFrom method)
class SiteADownloader {
public static Image getImage(URI uri) {
System.out.println("invoking SiteADownloader on "+uri);
Image i = null;
// logic for dowlnoading image from siteA
return i;
}
}
class SiteBDownloader {
public static Image getImage(URI uri) {
System.out.println("invoking SiteBDownloader on "+uri);
Image i = null;
// logic for dowlnoading image from siteB
return i;
}
}
// MAIN CLASS
class ImageDownloader {
public static Image getImageFrom(String serverName, URI uri) {
Image i = null;
try {
// load class
Class<?> c = Class.forName(serverName + "Downloader");
// find method to dowload img
Method m = c.getDeclaredMethod("getImage", URI.class);
// invoke method and store result (method should be invoked on
// object, in case of static methods they are invoked on class
// object stored earlier in c reference
i = (Image) m.invoke(c, uri);
} catch (NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException | ClassNotFoundException e) {
e.printStackTrace();
}
return i;
}
// time for test
public static void main(String[] args) {
try {
Image img = ImageDownloader.getImageFrom("SiteB", new URI(
"adress"));
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}

Custom URLClassLoader, NoClassDefFoundError when run

I've created my own URLClassLoader, and set it as the system classloader via java.system.class.loader. It's initialized and everything, but the classes I'm trying to load aren't found. Here's the URLClassLoader:
public class LibraryLoader extends URLClassLoader
{
public LibraryLoader(ClassLoader classLoader)
{
super(new URL[0], classLoader);
}
synchronized public void addJarToClasspath(String jarName) throws MalformedURLException, ClassNotFoundException
{
File filePath = new File(jarName);
URI uriPath = filePath.toURI();
URL urlPath = uriPath.toURL();
System.out.println(filePath.exists());
System.out.println(urlPath.getFile());
addURL(urlPath);
}
}
I've confirmed that the jar exists, and that the path is correct. This is how I call it in my program:
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
loader.addJarToClasspath("swt.jar");
This is the exception that I get (line 166 refers to the line at which I try to create a new Point:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/graphics/Point
at mp.MyProgram.loadArchitectureLibraries(MyProgram.java:116)
at mp.MyProgram.main(MyProgram.java:90)
Caused by: java.lang.ClassNotFoundException: org.eclipse.swt.graphics.Point
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 2 more
I even tried explicitly loading the class like so:
Class.forName("org.eclipse.swt.graphics.Point", false, loader);
What might be causing this? Shouldn't it "just work"?
Update: Here's the important code from MyProgram
public class MyProgram
{
// ...
public static void main(String[] args)
{
loadArchitectureLibraries();
// ...
}
public static void loadArchitectureLibraries()
{
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
String architecture = System.getProperty("os.arch");
try {
if (architecture.contains("64")) {
loader.addJarToClasspath("swt-3.6.1-win32-win32-x86_64.jar");
} else {
loader.addJarToClasspath("swt-3.6.1-win32-win32-x86.jar");
}
Class.forName("org.eclipse.swt.graphics.Point", false, loader);
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
} catch (Exception exception) {
exception.printStackTrace();
System.out.println("Could not load SWT library");
System.exit(1);
}
}
}
Update 2: Here's an SSCCE: http://nucleussystems.com/files/myprogram.zip . Call java -Djava.system.class.loader=mp.LibraryLoader -jar myprogram.jar.
I would have to agree with the comments on this question. Based on the code you have provided, it would appear that you are getting the error due to the JAR files not being where you expect them to be. As mentioned by #Andrew, you are not checking the existence of the file in your addJarToClasspath method. As a result, if the file does not exist, you will receive a ClassNotFound exception as you are seeing. I verified this problem by taking your ClassLoader logic and passing it a valid and an invalid JAR. When a valid JAR/path was provided, the ClassLoader loaded the class as expected. When an invalid JAR/path was specified, I received the error you mentioned. The URLClassLoader does not throw an exception if an URL is specified that does not point to a valid file.
To validate the scenario, print out the path of the full path of your File and see if it is correct for the execution environment.
Edit
It appears that even if you override the system ClassLoader, the VM will still use the default sun.misc.Launcher$AppClassLoader to load some classes. In my testing this includes the classes that are referenced from the main application. I'm sure there is a reason for this process, however, I am unable to ascertain it at this time. I have come up with a few solutions for you:
Use a script to detect the environment and set the classpath accordingly. This is perhaps the simplest solution, but one you may or may not want to take based on your particular requirements.
Similar to what was mentioned in other answers, specifically load and execute your application using your custom ClassLoader. This does not mean creating a single class that will be loaded and then invoke your application. It means that any class that needs to interact with the dynamically loaded swt libraries and any classes that need to reference your application classes should be loaded from your custom ClassLoader. Any application dependencies, such as log4j, etc, can be referenced by the default application ClassLoader. Here is an example of how this would work:
JAR 1 (launcher.jar):
public class AppLauncher {
public static void main(String… args) throws Exception {
ClassLoader loader = initClassLoader();
Class<?> mpClass = loader.loadClass("mp.MyProgram");
// using Runnable as an example of how it can be done
Runnable mpClass = (Runnable) mpClass.newInstance();
}
public static ClassLoader initClassLoader() {
// assuming still configured as system classloader, could also be initialized directly
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
// add the main application jar to the classpath.
// You may want to dynamically determine this value (lib folder) or pass it in as a parameter
loader.addJarToClasspath("myapp.jar");
String architecture = System.getProperty("os.arch");
try {
if (architecture.contains("64")) {
loader.addJarToClasspath("swt-3.6.1-win32-win32-x86_64.jar");
} else {
loader.addJarToClasspath("swt-3.6.1-win32-win32-x86.jar");
}
Class.forName("org.eclipse.swt.graphics.Point", false, loader);
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
} catch (Exception exception) {
exception.printStackTrace();
System.out.println("Could not load SWT library");
System.exit(1);
}
return loader;
}
JAR 2 (myapp.jar): Includes all class which depend on swt
public class MyProgram implements Runnable {
//…
public void run() {
// perform application execution
// this logic should now work
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0,0);
}
}
The AppLauncher class would be executed by the VM without the rest of your application being included in the execution Jar.
java -Djava.system.class.loader=test.LibraryLoader -cp <dependency jars>:launcher.jar mp.AppLauncher
I see that there have been updates to the other answers. Since I already had typed up the above comments, I figured that I should still post it for your perusal.
It's visible from a (few) mile(s) away you are not using the custom classloader beside Class.forName
The ClassNoDefFoundError occurs since the classloader that has loaded current class MyProgram attempts to load org.eclipse.swt.graphics.Point.
You need to load another class (call it launcher) via Class.forName and then start from there - implement some interface (even runnable will do) and call it.
edit
How to do it, a simplistic scenario.
1. Create another class called mp.loader.Launcher that implements Runnable like that.
public class Launcher implements Runnable{
public void run(){
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
//whatever, start from here.
}
}
2. Place it in another jar called swt-loader.jar.
in MyProgram class use:
loader.addJarToClasspath("swt-loader.jar");
Runnable r = (Runnable) Class.forName("mp.loader.Launcher", true, loader);
r.run();//there you have
Since the offending line is not the Class.forName but the actual initialization of an instance of Point, we'll have to make sure that the class, that tries to load the Point class, was created by the Library class loader. Therefore, I made some minor changes in the LibraryLoader accordingt to this blog entry
public class LibraryLoader extends URLClassLoader {
public LibraryLoader(ClassLoader classLoader) {
super(new URL[0], classLoader);
}
synchronized public void addJarToClasspath(String jarName)
throws MalformedURLException, ClassNotFoundException {
File filePath = new File(jarName);
URI uriPath = filePath.toURI();
URL urlPath = uriPath.toURL();
System.out.println(filePath.exists());
System.out.println(urlPath.getFile());
addURL(urlPath);
}
#Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if ("mp.MyProgram".equals(name)) {
return getClass(name);
}
return super.loadClass(name, resolve);
}
private Class<?> getClass(String name) throws ClassNotFoundException {
String file = name.replace('.', File.separatorChar) + ".class";
byte[] b = null;
try {
b = loadClassData(file);
Class<?> c = defineClass(name, b, 0, b.length);
resolveClass(c);
return c;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private byte[] loadClassData(String name) throws IOException {
InputStream stream = getClass().getClassLoader().getResourceAsStream(
name);
int size = stream.available();
byte buff[] = new byte[size];
DataInputStream in = new DataInputStream(stream);
in.readFully(buff);
in.close();
return buff;
}
}
In the program itself, we have to extract a new method since all the classes, that are used from within a method, seem to be loaded up-front:
public class MyProgram {
public static void main(String[] args) {
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
String architecture = System.getProperty("os.arch");
try {
loader.addJarToClasspath("swt.jar");
otherMethod();
} catch (Throwable exception) {
// println instead of logger because logging is useless at this level
exception.printStackTrace();
System.out.println("Could not load SWT library");
System.exit(1);
}
}
protected static void otherMethod() {
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
System.out.println("Works!");
}
}
That should work for you.

Categories