I'm attempting to:
1) Load an interface and implementation class from a given file location
2) Create a Proxy object with reflection that matches the interface, and directs all calls to the implementation class
This is later used for testing purposes using JUnit.
The Problem:
However, I seem to be having problems when I try to create the proxy object. I get the exception:
java.lang.IllegalArgumentException: interface Testing.Testable is not visible from class loader
...at Core.ProxyFactory.createProxy(ProxyFactory.java:26)
The line in question is as follows:
Object obj = Proxy.newProxyInstance(implementationClass.getClassLoader(), new Class[]{interfaceClass}, forwarder);
Class loading the right way?
I am loading the classes that I need using a URLClassLoader. The snippet for this is as follows:
URL url = new File(path).toURI().toURL();
URL[] urlList = {url};
// Create loader and load
ClassLoader classLoader = new URLClassLoader(urlList);
Class loadedClass = classLoader.loadClass (classname);
return loadedClass;
However, is this correct? This snippet gets repeated for each class file, and so I believe each time a new class loader is created. Could this be causing my problem? How can I resolve this?
Thanks in advance for any help you can give
Solved...
I was correct in my concerns that I was loading the classes in the wrong way. Because the classes depend on each other (e.g. one class uses another), they need to belong to the same classloader, or a child therefore of.
This problem can be solved by replacing use of the URLClassLoader with:
ClassLoader classLoader = new URLClassLoader(urlList);
Class[] classes = new Class[classNames.length];
for (int i = 0; i<classNames.length; i++) {
classes[i] = classLoader.loadClass(classNames[i]);
}
This allows you to load multiple classes using the same classloader, and seems to solve the problem!
Related
I was playing with classloaders in java and found the following behavior. I could logically reason out about this, but I'm not sure what I'm assuming is completely true. I'd like to know more formal explanation of this behavior.
What I was trying?
So I had the following code:
URL[] classURLs = {new URL("file://C:/Users/HP/IdeaProjects/test/out/production/test/")};
URLClassLoader urlClassLoader = new URLClassLoader(classURLs, null);
Class<?> personClass = urlClassLoader.loadClass("com.test.Person");
// the following line will give a ClassCastException
Person p = (Person) personClass.getDeclaredConstructor().newInstance();
Now the last line gives me a ClassCastException.
My reasoning (guess) about why I'm getting a ClassCastException: The classloader of personClass is urlClassLoader whereas the classloader of Person class is actually application class loader or system class loader (please correct me if I'm wrong). These class loaders don't match and I'm getting a ClassCastException. (I'm here assuming that when typecasting a check is performed on the classloaders)
So now I continue exploring and alter the construction of URLClassLoader in the following way:
URLClassLoader urlClassLoader = new URLClassLoader(classURLs, Main.class.getClassLoader());
Here Main is the enclosing class. The above line saves me from a ClassCastException.
My reasoning (guess) about this: As now the urlClassLoader has application class loader as its parent (this application class loader is same that is used to load Person class), while trying to cast, Java check if the classloaders match and this check continues with the parent of the urlClassLoader, after going one step up the classloaders match and there is no ClassCastException.
I assume that the classloader of the class of the object to be typecasted is checked against the classloader of the class into which you need to typecast and if this don't match the parent of the classloader of the class of object is tried for the match and this continues.
Please correct me if I'm wrong at any point and also provide pointers to the formal documentation of this behavior.
I have seen this link, but this don't provide the details I've asked.
The formal documentation for the behaviour that you observe is in the ClassLoader#loadClass() documentation:
Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:
Invoke findLoadedClass(String) to check if the class has already been loaded.
Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
Invoke the findClass(String) method to find the class.
If you specify a parent class loader your URLClassLoader checks the parent class loader for the class before trying to load the class itself, which means that it will find the class from your application class path.
So if you set the parent class loader, this line:
Class<?> personClass = urlClassLoader.loadClass("com.test.Person");
behaves the same as
Class<?> personClass = Main.class.getClassLoader().loadClass("com.test.Person");
if the class com.test.Person is available on the application class loader (which it must be, otherwise your Main class cannot be loaded).
You are loading the classes dynamically, thus, since you're able to compile class "Person", it means you're loading the same class twice, resulting in class cast exception.
Remove the library from your classpath and you won't get this error however, you also will loose access to the Person object.
Its still there when you load it, but the way to access it would be via Reflection, and you'll have to store the "Person" object as an "Object".
This question is slightly different from the previous error reported via Unable to invoke method by java reflection: NoSuchMethodException
Signature of overloading methods follow as below in target class, my objective is to invoke public List run(FileInput,StreamOutput)dynamically.
public List run(String, String);
public List run(FileInput,StreamOutput);
Case 1: When dynamically loading a method using reflection api when target class(jar) is present in CLASS_PATH - method is accessible.
Class mapClass = loader.loadClass("com.sample.test.TrackingService");
FileInput input = new FileInput(inputFileFullPath);
StreamOutput output = new StreamOutput(new java.io.ByteArrayOutputStream());
Object mapObj = mapClass.newInstance();
Class params[]={ FileInput.class,StreamOutput.class};
Method m = mapClass.getDeclaredMethod("run", params);
outputList = (List) m.invoke(mapObj, input,output);`
Case 2: On top of the same code in Case 1, instead of placing jar in CLASS_PATH, but loaded via URLClassLoader then it throws NoSuchMethodException
Code block
URL urls[] = {new URL("file:/opt/jars/Tracking.jar")};
URLClassLoader loader = new URLClassLoader(urls);
Class mapClass = loader.loadClass("com.sample.test.TrackingService",true,loader);
FileInput input = new FileInput(inputFileFullPath);
StreamOutput output = new StreamOutput(new java.io.ByteArrayOutputStream());
Object mapObj = mapClass.newInstance();
Class params[]={ FileInput.class,StreamOutput.class};
Method m = mapClass.getDeclaredMethod("run", params);
outputList = (List) m.invoke(mapObj, input,output);
Exception is thrown at line Method m = mapClass.getDeclaredMethod("run", params);
Question 1: Is something missing here when loading class via URLClassLoader ?
Question 2: Any recommendation on loading class definition into JVM via custom class. I mean, achieving same level as jar file present in CLASS_PATH ? Part of our requirement and limitation we can't place jar under CLASS_PATH or add jar path to CLASS_PATH
All that should change between the two code(One using ClassLoader and other using URLClassLoader), is the instantiation, i.e.
This:
ClassLoader loader = getClass().getClassLoader();
Class<?> myLib = loader.loadClass("com.chetan.loader.MyLib");
Should be replaced by:
URL url[] = {new URL("file:C:\\Chetan\\mylib-0.0.1-SNAPSHOT.jar")};
URLClassLoader loader = new URLClassLoader(url);
Class<?> myLib = loader.loadClass("com.chetan.loader.MyLib");
Here is what I recommend, though you might have tried these things, but in case you haven't:
Unpack the Jar /opt/jars/Tracking.jar (You can open it via Winzip or other similar utility), and then decompile the class TrackingService - Check if it does contain the method you are trying to call?
Give you code a fresh look / Simulate - I will suggest create a small class with the two method variants. Pack it in a jar. create a test loader class and try out using URLClassLoader - This will give you the confidence that the code you have written works. You can then look for other aspects of the problem.
In any case, let me know if above suggestions worked. In case you are not able to make the test class work as well, do share the code so that fellow members here can better understand what you are trying to do.
It worked when the class loader changed as shown below.
URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:/opt/jars/Tracking.jar") }, this.getClass().getClassLoader());
Rest everything same... as I said, with-out adding the current class loader also worked when run in eclipse.
I am trying to make a code to dynamically load a class and use it to create new instances and to run casts. I can make an instance from the newly loaded class using this method: myClass.newInstance();. But I cannot use it as a type. For example: myClass myObj = new myClass(); It doesn't work. Is it possible to perform somehow?
This is the code I was trying to make:
URL classUrl;
classUrl = new URL("file:///C:/classes/");
URL[] classUrls = { classUrl };
URLClassLoader ucl = new URLClassLoader(classUrls);
Class c = ucl.loadClass("Operation");
Class MyIn = ucl.loadClass("MyInter");
Object o = c.newInstance(); //IT WORKS
System.out.println(((MyIn) o).sum(2, 4)); //IT DOES NOT WORK. Message: MyIn cannot be resolved to a type
Since you don't know MyIn at compile time, you need to use reflection not only to instantiate the class, as you do, but also to call methods.
Something along the lines of:
MyIn.getDeclaredMethod("sum",Integer.TYPE,Integer.TYPE).invoke(o,2,4);
See for example https://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html
You cannot directly use a class at compile-time that you load later at run-time.
However, you can work directly through one or more interfaces implemented by the loaded classes. Typically the interfaces themselves are included in the compile-time classpath of both the loading code and the loaded code.
The shared interfaces define the expected interaction at compile-time. The loaded code defines a behavior at run-time.
For example, you could load this dynamically loaded class:
public class Operation implements MyInterface { ... }
And then interact with it through a shared interface:
Class c = ucl.loadClass("mypackage.Operation");
Object o = c.newInstance(); //IT WORKS
MyInterface operation = (MyInterface) o;
Now you can interact directly with the methods through the shared interface.
System.out.println( operation.sum(2, 4) );
Constructors
For example: myClass myObj = new myClass(); It doesn't work. Is it possible to perform somehow?
You can create a factory with named constructors in the dynamically loaded code. The factory would also implement a shared interface.
Class cFactory = ucl.loadClass("mypackage.MyFactory");
MyFactoryInterface factory = (MyFactoryInterface) cFactory.newInstance();
MyInterface myObj = factory.makeOperation( );
I'm trying to use Class.forName to dynamically load a class from a .jar file on the filesystem at runtime. The class I am trying to load implements an interface in another .jar file, so I am using my own URLClassLoader to reference the two .jars.
The code works when it is called not in the context of the web app (I have tested this by copying and pasting the method into a separate program and calling it from main). However, when I run/debug the web app (I'm using NetBeans) the code fails. The newInstance method throws a ClassCastException when I try to cast the instance to the interface specified in my jar_file_dependencies.jar.
Here is the relevant code if this helps:
File gameJar = new File("C:\\file_path\\jar_file.jar");
File gameDependenciesJar = new File("C:\\file_path\\jar_file_dependencies.jar");
URLClassLoader cl = new URLClassLoader(new URL[]
{
gameJar.toURI().toURL(),
gameDependenciesJar.toURI().toURL()
});
Class clazz = Class.forName("MyClass", true, cl);
IMyClass myClass = (IMyClass)clazz.newInstance();
System.out.println(game);
} catch (Exception e)
{
System.out.println(e.getMessage());
}
Any suggestions as to why this code is working in one program and not another would be greatly appreciated.
Many thanks,
Dan
short answer without going into too many of the hairy details: one or both of the gameJar and gameDependenciesJar probably contain a definition of the IMyClass class/interface. the rule of thumb when using child classloaders is that the child classloader should not contain any of the "shared" classes--these should exist only in the parent classloader.
partial explanation: Web app classloaders usually have different delegation policies from normal classloaders. often they prefer the child's class to the parent's. normal classloaders generally prefer the parent's class to the child's. in your web app, you are ending up with 2 separate definitions of the IMyClass class (one def in the parent classloader, one in the child). in your normal app, the IMyClass definition in the child classloader is being ignored, so only one definition gets loaded (in the parent classloader), and everything is happy.
Maybe this will help, (untested):
ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
if (clsLoader == null) {
clsLoader = this.getClass().getClassLoader();
}
URLClassLoader cl = new URLClassLoader(new URL[]
{
gameJar.toURI().toURL(),
gameDependenciesJar.toURI().toURL()
}, clsLoader);
Also, you should pass a full declarative name of the class MyClass instead of just calling it MyClass in Class.forName().
E.g.
Class clazz = Class.forName("com.xxxx.yyy.MyClass", true, cl);
I am developing a web application.
The web application generates java classes on the fly. For example it generates class com.people.Customer.java
In my code, I dynamically compile this to get com.people.Customer.class and store in some directory say repository/com/people/Customer.class which is not on the classpath of my application server.My application server(I am using WebSphere Application Server/Apache Tomcat etc) picks up the classes from the WEB-INF/classes directory. The Classloader would use this to load the classes.
After compilation I need to load this class so that it becomes accessible to other classes using it after its creation.
When I use Thread.currentThread().getContextClassLoader().loadClass(com.people.Customer) obviously the Classloader is not able to load the class, since its not on the classpath(not in WEB-INF/classes). Due to similar reasons, getResource(..) or getResourceAsStream(..) also does not work.
I need a way to :
Read the class Customer.class maybe as a stream (or any other way would do) and then load it. Following are the constraints:
I cannot add the repository folder to the WEB-INF/classes folder.
I cannot create a new Custom ClassLoader. If I create a new ClassLoader and this loads the class, it will not be accessible to its parent ClassLoader.
Is there any way of achieving this?
If not this, in the worse case, is there a way of overriding the default class loader with a custom class loader for web applications the same classloader should be used to load applications throughout entire lifecycle of my web application.
Appreciate any solution :)
You need a custom class loader to do this, and in this classloader you need to re-define a method findClass(String name)
An example:
public class CustomClassLoader extends ClassLoader {
final String basePath = "/your/base/path/to/directory/named/repository/";
#Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
String fullName = name.replace('.', '/');
fullName += ".class";
String path = basePath + fullName ;
try {
FileInputStream fis = new FileInputStream(path);
byte[] data = new byte[fis.available()];
fis.read(data);
Class<?> res = defineClass(name, data, 0, data.length);
fis.close();
return res;
} catch(Exception e) {
return super.findClass(name);
}
}
}
Then, you'll be load classes from custom location. For example:
Class<?> clazz = Class.forName("my.pretty.Clazz", true, new CustomClassLoader());
Object obj = clazz.newInstance();
Doing this, you tell JVM that class named my.pretty.Clazz should be loaded by your custom class loader, which knows how and where from to load your custom class.
It resolves full class name (like my.pretty.Clazz) to file name (in our case: /your/base/path/to/directory/named/repository/my/pretty/Clazz.class), then loads obtained resource as a byte array, and finally converts this array to a Class instance.
This example is very simple and demonstrates a general technique about how to load custom classes as in your case.
I suggest you to read some articles about class loading, for example this one.
Short answer: No
Without a custom ClassLoader, you cannot dynamically load classes.
However, your assumption that you cannot use a custom ClassLoader because your other objects loaded by the WebApp ClassLoader would be unable to use these newly loaded classes is incorrect. All you need is a generic way to use these newly created classes - like a common interface or a meta-description (Beans Introspector for accessing bean properties).
But if you are using third-party libraries like Hibernate and you are dynamically loading entities at runtime which are to be persisted, then you will have a hard time, but imho it is possible.
Sure you can do this. Just get the web classloader and call the defineClass() method using reflection (it is protected, so be sure to call setAccessible(true) on the method. defineClass() takes a byte array, so it doesn't make any difference where you class is from. Make sure that the class name is unique and you're loading it only once, or you'll have complicated classloading problems.