Reflection type mismatch - java

I have compiled a class at runtime which I would like to use on the fly, provided that its constructor takes one parameter
package com.notmycompany;
import com.mycompany.Manager;
import com.mycompany.Processor;
import com.mycompany.Event;
public class CustomProcessor extends Processor {
public CustomProcessor( Manager m) {
super( m);
}
#Override
public void process( Event evt) {
// Do you own stuff
System.out.println( "My Own Stuff");
}
}
Compilation goes fine and I can load the class right away. But the constructor is giving me a hard time.
Class<?> clazz = urlClassLoader.loadClass("com.notmycompany.CustomProcessor");
Constructor<?> constructor = clazz.getConstructor( com.mycompany.Manager.class);
this.customProcessor = (Processor) constructor.newInstance( this.manager);
In this case, getConstructor throws a NoSuchMethodException
I tried using getConstructors instead, which only gets me one step further with IllegalArgumentException during newInstance call (of course this.manager is com.mycompany.Manager)
Constructor<?> list[] = clazz.getConstructors();
Constructor<?> constructor = list[0];
this.customProcessor = (Processor) constructor.newInstance( this.manager);
Watever I do, there is a mismatch between Manager object at runtime and compilation
How can I fix this constructor signature?
Edit 1: getParameterTypes output
for( Class<?> c : constructor.getParameterTypes()) {
System.out.println( c);
}
outputs
class com.mycompany.Manager
Edit 2: I removed constructor parameter as a temporary workaround
Now the code throws ClassCastException complaining that com.notmycompany.CustomProcessor cannot be cast to com.mycompany.Processor when constructor is invoked:
Constructor<?> constructor = clazz.getConstructor();
this.customProcessor = (Processor) constructor.newInstance();
This all seems to be part of the same problem where runtime classes seem inconsistent with compilation's, although names match.

Your CustomProcessor class has no constructor because the name of the method you believe to be your constructor is different.
public CustomLatencyProcessor(Manager m) {
super(m);
}
Should be changed to
public CustomProcessor(Manager m) {
super(m);
}
Because the name of your class is CustomProcessor. Constructor's names must match the name of their containing class exactly.

I have been able to get it to work eventually after using a URL that uses the currentThread as parent (as opposed to a URLClassLoader created from scratch with URLs)
URLClassLoader ucl = (URLClassLoader)Thread.currentThread().getContextClassLoader();
URLClassLoader ucl2 = new URLClassLoader( new URL[] { new URL( "file://d:/temp/")},ucl);
Class<?> clazz = ucl2.loadClass("com.notmycompany.CustomProcessor");
I hope this can save you 2 days!

Related

Java Class.forName won't compile. Getting "cannot find symbol symbol : method"

I'm trying to use Class.forName and my Intellij is throwing a compile error. My IntelliJ highlights "theResponse" in red (in testMethod) and gives me this error:
cannot find symbol symbol : method
Here is the code (and test) I'm working with...
package http.response;
public class TestClass {
public TestClass() {
PublicRoute publicRoute = new PublicRoute();
}
public String testMethod() throws ClassNotFoundException {
Class c = Class.forName("http.response.PublicRoute");
return c.theResponse("hi");
}
}
package http.response;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
public class TestClassTest {
#Test
public void test() throws ClassNotFoundException {
TestClass testClass = new TestClass();
assertEquals("public", testClass.testMethod());
}
}
UPDATE: What I was trying to do was "polymorphically" call theResponse from the class that is returned as a String from a HashMap. How would I do this? I'm (loosely) following this example but I didn't understand it fully (http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism). Here is a simplified version of what I'm trying to do. Hopefully that makes sense.
package http.response;
import java.util.HashMap;
public class TestClass {
HashMap map;
public TestClass(HashMap map) {
this.map = map;
}
public String testMethod(String lookupValue) throws ClassNotFoundException {
String className = map.get(lookupValue);
Class c = Class.forName("http.response." + className);
return c.theResponse();
}
}
Class.forName() returns an object of type java.lang.Class. java.lang.Class has no method theResponse, as you can see from its Javadoc.
It sounds like what you actually want to do is construct an instance of the PublicRoute class, and call the method on the instance. But you've already constructed such an instance: it's the publicRoute variable you create in your constructor. Why not just use that object instead?
Edit: Ah, I see what you're trying to do. You basically want a form of the Service Locator pattern.
Create an interface, like so:
public interface ResponseProvider {
String theResponse();
}
Then make all your classes implement that interface:
public class PublicRoute implements ResponseProvider {
#Override
public String theResponse() {
// do whatever
}
}
Then, when you load your Class<?>, you can use the asSubclass() method to turn your Class<?> into a Class<? extends ResponseProvider> -- then newInstance() will give you back a ResponseProvider object that you can call theResponse() on, like so:
String className = ...;
Class<?> klass = Class.forName(className);
Class<? extends ResponseProvider> responseProviderClass
= klass.asSubclass(ResponseProvider.class);
ResponseProvider responseProvider = responseProviderClass.newInstance();
return responseProvider.theResponse();
But don't do that by hand -- instead, use the java.util.ServiceLoader class, which is designed for exactly this purpose. You create a special META-INF/services/com.my.package.ResponseProvider file, with a list of all the possible classes that implement that interface, and then ServiceLoader can give you back instances of each of them.
But... consider not doing that, either. The types of problems that you can solve with the Service Locator pattern are often better solved by using Dependency Injection (see also my answer to another question about Dependency Injection). The Guice DI framework, for example, offers a feature called multibindings which looks like exactly what you need.
If theResponse() belongs to http.response.PublicRoute then it should have been
Class c = Class.forName("http.response.PublicRoute");
return ((PublicRoute) c.newInstance()).theResponse("hi");
But, then there's really no need for Class.forName() as you could use constructor as
return new PublicRoute().theResponse("hi");
The class Class does not have a method named theResponse. From the rest of your code, it doesn't look like you should be using reflection here; you're already referring statically to the PublicRoute class, so there's no point loading it dynamically.
I think you just want to write either this:
return PublicRoute.theResponse("hi");
or this:
return new PublicRoute().theResponse("hi");
(depending whether theResponse is a static method or an instance method).
Let me see if I understand what you're trying to do. You've got a hashmap that will contain a list of classes that you're going to try to call the theResponse(String response) method on, right? I'm assuming you won't know the String that will be put into the hashmap either, right?
Others are right in that you can't just do:
Class c = Class.forName("http.response.PublicRoute");
c.theResponse("hi"); // errors because c has no knowledge of theResponse()
You'll need to cast c to http.response.PublicRoute but then as #Ravi Thapliyal pointed out, you won't need Class.forName anymore! You've got a hashmap of names that could potentially be anything so this won't work.
If I'm understanding you correctly to do what you need, you'll need to use reflection in order to attempt to instance the class then call the method on it.
Here's how you'd do it assuming the theResponse method is a public non-static method and has only 1 parameter.
// Declare the parameter type
Class[] paramString = new Class[1];
paramString[0] = String.class;
String className = map.get(lookupValue);
// Instance the class
Class cls = Class.forName("http.response." + className);
Object obj = cls.newInstance();
// Call the method and pass it the String parameter
method = cls.getDeclaredMethod("theResponse", paramString);
method.invoke(obj, new String("hi"));
Of course you'll need to handle Exceptions but you'd surround the above code with the loop for your hashmap.
I hope this helps!

Passing Parameters Between ClassLoaders Via Reflection API

I'm using a custom Classloader to create and return an instance of a class, this seems to work ok however when I try to call a method (via the Reflection API) and pass in a custom object as described below I get a NoSuchMethodException:
Supposing that the custom class loader creates and returns an instance like so:
Object obj = customClassLoader.load(String className,Class[] paramTypes,Object[] param)
Then I call a method (via reflection) and pass in a custom object:
NOTE: THIS IS THE LINE CAUSING THE ERROR
Method m = obj.getClass.getDeclaredMethod("mName",new Class[]{aCustomObject.class})
m.invoke(obj,new Object[]{new CustomObject() })
I'm stumped as to what could be causing the exception since a method definitely does exist which takes the specified custom object, I have confirmed this by using reflection to list all methods.
How is your custom loader's load() method instantiating the object it is to return? Maybe the NoSuchMethodException arises during trying to find the correct constructor?
This example seems to work out OK:
package com.pholser;
import java.lang.reflect.Method;
public class ClassLoading {
public static class CustomLoader extends ClassLoader {
public Object load(String className, Class<?>[] paramTypes, Object[] params) throws Exception {
Class<?> loaded = loadClass(className);
return loaded.getConstructor(paramTypes).newInstance(params);
}
}
public static class ACustomObject {
}
public void foo(ACustomObject a) {
System.out.println("foo");
}
public static Object newCustomObject() throws Exception {
return new CustomLoader().load("com.pholser.ClassLoading$ACustomObject", new Class<?>[0], new Object[0]);
}
public static void main(String[] args) throws Exception {
ClassLoading obj = new ClassLoading();
Method m = obj.getClass().getDeclaredMethod("foo", ACustomObject.class);
m.invoke(obj, newCustomObject());
}
}

Overriding default accessor method across different classloaders breaks polymorphism

I come across to a strange behavior while trying to override a method with default accessor (ex: void run()).
According to Java spec, a class can use or override default members of base class if classes belongs to the same package.
Everything works correctly while all classes loaded from the same classloader.
But if I try to load a subclass from separate classloader then polymorphism don't work.
Here is sample:
App.java:
import java.net.*;
import java.lang.reflect.Method;
public class App {
public static class Base {
void run() {
System.out.println("error");
}
}
public static class Inside extends Base {
#Override
void run() {
System.out.println("ok. inside");
}
}
public static void main(String[] args) throws Exception {
{
Base p = (Base) Class.forName(Inside.class.getName()).newInstance();
System.out.println(p.getClass());
p.run();
} {
// path to Outside.class
URL[] url = { new URL("file:/home/mart/workspace6/test2/bin/") };
URLClassLoader ucl = URLClassLoader.newInstance(url);
final Base p = (Base) ucl.loadClass("Outside").newInstance();
System.out.println(p.getClass());
p.run();
// try reflection
Method m = p.getClass().getDeclaredMethod("run");
m.setAccessible(true);
m.invoke(p);
}
}
}
Outside.java: should be in separate folder. otherwise classloader will be the same
public class Outside extends App.Base {
#Override
void run() {
System.out.println("ok. outside");
}
}
The output:
class App$Inside
ok. inside
class Outside
error
ok. outside
So then I call Outside#run() I got Base#run() ("error" in output). Reflections works correctly.
Whats wrong? Or is it expected behavior?
Can I go around this problem somehow?
From Java Virtual Machine Specification:
5.3 Creation and Loading
...
At run time, a class or interface is
determined not by its name alone, but
by a pair: its fully qualified name
and its defining class loader. Each
such class or interface belongs to a
single runtime package. The runtime
package of a class or interface is
determined by the package name and
defining class loader of the class or
interface.
5.4.4 Access Control
...
A field or method R is accessible to a class
or interface D if and only if any of
the following conditions is true:
...
R is either protected or package private (that is, neither public nor
protected nor private), and is
declared by a class in the same
runtime package as D.
The Java Language Specification mandates that a class can only override methods that it can access. If the super class method is not accessible, it is shadowed rather than overridden.
Reflection "works" because you ask Outside.class for its run method. If you ask Base.class instead, you'll get the super implementation:
Method m = Base.class.getDeclaredMethod("run");
m.setAccessible(true);
m.invoke(p);
You can verify that the method is deemed inaccessible by doing:
public class Outside extends Base {
#Override
public void run() {
System.out.println("Outside.");
super.run(); // throws an IllegalAccessError
}
}
So, why is the method not accessible? I am not totally sure, but I suspect that just like equally named classes loaded by different class loaders result in different runtime classes, equally named packages loaded by different class loaders result in different runtime packages.
Edit: Actually, the reflection API says that it's the same package:
Base.class.getPackage() == p.getClass().getPackage() // true
I found the (hack) way to load external class in main classloader so this problem is gone.
Read a class as bytes and invoke protected ClassLoader#defineClass method.
code:
URL[] url = { new URL("file:/home/mart/workspace6/test2/bin/") };
URLClassLoader ucl = URLClassLoader.newInstance(url);
InputStream is = ucl.getResourceAsStream("Outside.class");
byte[] bytes = new byte[is.available()];
is.read(bytes);
Method m = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
m.setAccessible(true);
Class<Base> outsideClass = (Class<Base>) m.invoke(Base.class.getClassLoader(), "Outside", bytes, 0, bytes.length);
Base p = outsideClass.newInstance();
System.out.println(p.getClass());
p.run();
outputs ok. outside as expected.

Using Java reflection to create eval() method

I have a question about reflection
I am trying to have some kind of eval() method. So i can call for example:
eval("test('woohoo')");
Now I understand the there is no eval method in java but there is reflection. I made the following code:
String s = "test";
Class cl = Class.forName("Main");
Method method = cl.getMethod(s, String.class);
method.invoke(null, "woohoo");
This works perfectly (of course there is a try, catch block around this code). It runs the test method. However I want to call multiple methods who all have different parameters.
I don't know what parameters these are (so not only String.class). But how is this possible? how
can I get the parameter types of a method ?
I know of the following method:
Class[] parameterTypes = method.getParameterTypes();
But that will return the parameterTypes of the method I just selected! with the following statement:
Method method = cl.getMethod(s, String.class);
Any help would be appreciated !
You will need to call Class.getMethods() and iterate through them looking for the correct function.
For (Method method : clazz.getMethods()) {
if (method.getName().equals("...")) {
...
}
}
The reason for this is that there can be multiple methods with the same name and different parameter types (ie the method name is overloaded).
getMethods() returns all the public methods in the class, including those from superclasses. An alternative is Class.getDeclaredMethods(), which returns all methods in that class only.
You can loop over all methods of a class using:
cls.getMethods(); // gets all public methods (from the whole class hierarchy)
or
cls.getDeclaredMethods(); // get all methods declared by this class
.
for (Method method : cls.getMethods()) {
// make your checks and calls here
}
You can use getMethods() which returns an array of all methods of a class.
Inside the loop you can inspect the parameters of each method.
for(Method m : cl.getMethods()) {
Class<?>[] params = m.getParameterTypes();
...
}
Otherwise you can use getDelcaredMethods() which will allow you to "see" private methods (but not inherited methods). Note that if you want to invoke a private methods you must first apply setAccessible(boolean flag) on it:
for(Method m : cl.getDelcaredMethods()) {
m.setAccessible(true);
Class<?>[] params = m.getParameterTypes();
...
}
Ok thanxs to all the people who answered my question here the final solution:
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args){
String func = "test";
Object arguments[] = {"this is ", "really cool"};
try{
Class cl = Class.forName("Main");
for (Method method : cl.getMethods()){
if(method.getName().equals(func)){
method.invoke(null, arguments);
}
}
} catch (Exception ioe){
System.out.println(ioe);
}
}
public static void test(String s, String b){
System.out.println(s+b);
}
}

How does one access a method from an external jar at runtime?

This is a continuation of the question posted in: How to load a jar file at runtime
I am uncertain as to how to continue to the method invocation level. From my understanding,
from the clazz object, I would used getMethod or getDeclaredMethod to get a Method object from which I would call invoke. Of course, invoke requires an instance. Would that then be what is called doRun in the example code?
Do I need to perform the doRun.run() method call even though I want to execute a different method than main (assuming that it is main on the doRun object that is called with the run invocation)?
Just for more clarification of the original post, I ask:
Does doRun.run() start a new thread executing the instance of the class object of type clazz?
Thanks for helping to clear this up for me.
I did look at "how-should-i-load-jars-dynamically-at-runtime" (sorry, only allowed one hyperlink), however this looked to violate the Class.newInstance evilness admonition in the first post I referenced.
Here is some reflection code that doesn't cast to an interface:
public class ReflectionDemo {
public void print(String str, int value) {
System.out.println(str);
System.out.println(value);
}
public static int getNumber() { return 42; }
public static void main(String[] args) throws Exception {
Class<?> clazz = ReflectionDemo.class;
// static call
Method getNumber = clazz.getMethod("getNumber");
int i = (Integer) getNumber.invoke(null /* static */);
// instance call
Constructor<?> ctor = clazz.getConstructor();
Object instance = ctor.newInstance();
Method print = clazz.getMethod("print", String.class, Integer.TYPE);
print.invoke(instance, "Hello, World!", i);
}
}
Writing the reflected classes to an interface known by the consumer code (as in the example) is generally better because it allows you to avoid reflection and take advantage of the Java type system. Reflection should only be used when you have no choice.
The code example
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { yourURL },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<? extends Runnable> runClass = clazz.asSubclass(Runnable.class);
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
doRun.run();
assumes that the class you are loading implements a particular interface Runnable, and therefore it's reasonale to cast to that type using asSubclass() and invoke run().
What do you know about the classes you are loading? Can you assume that they implement a particualr interface? If so adjust the asSubClass() line to reference the interafce you prefer.
Then, yes if you are working with instance methods create an instance using the contructor, ctor in the example.
There is no starting of a thread in the example. Creating a new thread would just have needed a couple of lines more code
Thread myThread = new Thread(doRun);
myThread.start();
Sample Program:
Project Printer:
public class Printer {
public void display(String printtext)
{
System.out.println(printtext);
}
}
This project is exported as Printer.jar.
Printer Class has method display() which takes string as input.
Invoking code:
URL url = new URL("file:Printer.jar");
URLClassLoader loader = new URLClassLoader (new URL[] {url});
Class<?> cl = Class.forName ("Printer", true, loader);
String printString = "Print this";
Method printit = cl.getMethod("display", String.class);
Constructor<?> ctor = cl.getConstructor(); //One has to pass arguments if constructor takes input arguments.
Object instance = ctor.newInstance();
printit.invoke(instance, printString);
loader.close ();
Output:
Print this

Categories

Resources