Java Reflection: "java.lang.NoSuchMethodException" - java

I am trying to get the method from another class using reflection, but for some reason it keeps on giving me a no such method exception. These are the classes im using:
ScriptTable class:
for(Class<?> script: Scripts) {
System.out.println(script.getName());
try {
Method c = script.getMethod("scriptInfo()", script);
} catch(Exception e) {
e.printStackTrace();
}
}
DummyScript class
public String[] scriptInfo() {
String[] ScriptInfo = {"DummyScript", "Chris", "Does nothing atm"};
return ScriptInfo;
}

This is your problem:
script.getMethod("scriptInfo()", script);
change it to:
script.getMethod("scriptInfo");
and look here to see why:
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getMethod%28java.lang.String,%20java.lang.Class...%29

Related

Java : Crash without exception

I'm trying to debug some code that look like this :
class MyClass {
public void myMethod(HashMap<String, String> inputMap) {
try {
ConcurrentHashMap<String, String> cm = new ConcurrentHashMap<>();
cm.putAll(inputMap);
try {
for (Object key : cm.keySet()) {
cm.put(key.toString(), "");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("After try catch");
} finally {
System.out.println("In finally");
}
}
}
By using the debugger in InteliJ I've figured out that this piece of code have an issue on the for loop line.
The execution pass from the for loop to the finally clause without passing in the catch clause neither the code after the try/catch.
The cm object isn't empty (there is around 30 elements in it).
I'm using java 7, System.getProperty("java.version") give 1.7.0_85
When I try to call the cm.keySet() mnually from the InteliJ debugger I have the following error message No such instance method: 'keySet'. But when I look at the javadoc of the ConcurentHashMap class this method should exists.
When I run cm.getClass().getDeclaredMethods() I see the method public java.util.Set java.util.concurrent.ConcurrentHashMap.keySet() in the methods list.
This code is not running on the main thread.
This don't display any error message in the console and I'm not able to catch an exception.
Does anyone have an idea of what could be the problem there? I've tried everything I could think of, and I'm out of options.
Edit problem fixed
The issue wasn't even in the code itself, it was that the compiler was updated from java 7 to java 8 without me noticing and without crashing during the compilation, while the java version that I use on my server was java 7. Since I didn't have the possibility to change neither the compiler version neither the version on the server, I've rewritten the code in another way so that it will work on both version.
It gives something like this :
class MyClass {
public void myMethod(HashMap<String, String> inputMap) {
try {
ConcurrentHashMap<String, String> cm = new ConcurrentHashMap<>();
cm.putAll(inputMap);
try {
Enumeration<String> keys = cm.keys();
while(keys.hasMoreElements()) {
String key = keys.nextElement();
cm.put(key, "");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("After try catch");
} finally {
System.out.println("In finally");
}
}
}
Ps: Thank you, #cyberbrain, I wouldn't have figured this out without your advice on catching Throwable rather than Exception.
Try replacing Object with String
class MyClass {
public void myMethod(HashMap<String, String> inputMap) {
try {
ConcurrentHashMap<String, String> cm = new ConcurrentHashMap<>(inputMap);
try {
for (String key : cm.keySet()) {
cm.put(key, "");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("After try catch");
} finally {
System.out.println("In finally");
}
}
}
You can also replace the entire for loop with the following:
cm.replaceAll((k, v) -> "");
edit: Realized lamdas wont work on JDK 1.7

Typecasting with a class that is protected

I am trying to override some class of vertx web project, since I have to change some of the features. So the tricky part comes here.
#Override
public void reroute(HttpMethod method, String path) {
int split = path.indexOf('?');
if (split == -1) {
split = path.indexOf('#');
}
if (split != -1) {
log.warn("Non path segment is not considered: " + path.substring(split));
// reroute is path based so we trim out the non url path parts
path = path.substring(0, split);
}
/*((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);*/
((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);
request.params().clear();
// we need to reset the normalized path
normalisedPath = null;
// we also need to reset any previous status
statusCode = -1;
// we need to reset any response headers
response().headers().clear();
// special header case cookies are parsed and cached
if (cookies != null) {
cookies.clear();
}
// reset the end handlers
if (headersEndHandlers != null) {
headersEndHandlers.clear();
}
if (bodyEndHandlers != null) {
bodyEndHandlers.clear();
}
failure = null;
restart();
}
This code throws me a compilation error saying:
'HttpServerRequestWrapper cannot be accessed from outside package'
I know for a fact that we can use reflection to create objects of a class that cannot be accessed. Can reflection be used in this case? How can I fix such an issue.
Any help will be much appreciated.
In java 8 and/or without modules it is possible to just place class like that in same package as original one to get access to all package-default classes.
Otherwise you need to use reflections like in other response, but I would add that it is good idea to cache that Class and Method instance, as using Class.forName and clazz.getDeclaredMethod each time will slowdown code.
What about getting the Class object and then calling the methods on your specific (uncasted) object?
I assume request is a class attribute of type HttpServerRequestWrapper. Then, this is what I suggest:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
...
private final Method setMethod;
private final Method setPath;
public MyConstructor() {
Method tmp1 = null, tmp2 = null;
try {
final Class<?> clazz = Class.forName("io.vertx.ext.web.impl.HttpServerRequestWrapper");
tmp1 = clazz.getMethod("setMethod", HttpMethod.class);
tmp1.setAccessible(true);
tmp2 = clazz.getMethod("setPath", String.class);
tmp2.setAccessible(true);
} catch (ClassNotFoundException e) {
// do something
} catch (NoSuchMethodException e) {
// do something
} catch (SecurityException e) {
// do something
}
this.setMethod = tmp1;
this.setPath = tmp2;
}
...
#Override
public void reroute(HttpMethod method, String path) {
...
try {
this.setMethod.invoke(request, method);
this.setPath.invoke(request, path);
} catch (IllegalAccessException e) {
// do something
} catch (IllegalArgumentException e) {
// do something
} catch (InvocationTargetException e) {
// do something
}
...
}
EDIT: I updated this answer based on #GotoFinal's suggestion.
It looks like HttpServerRequestWrapper implements HttpServerRequest. So, you can change "HttpServerRequestWrapper" to "HttpServerRequest" in your code. But remember that by doing so, you'll only be able to call methods specified in the interface.
You can see those methods in https://vertx.io/docs/apidocs/io/vertx/rxjava/core/http/HttpServerRequest.html.

fail using a variable to call a method in java-android

I've tried using a variable to invoke a java method, using method.invoke(), as suggested in this example. But it seems there should be an object or something as a parameter in method.invoke(). I've tried using null, but the method didn't get invoked. My code is as follows:
String ACTION = "cart";
Method method = SolverService.class.getDeclaredMethod("Method" + ACTION);
method.invoke(null);
I've got a method as:
public void Methodcart(){
Toast.makeText(this,"Method called",Toast.LENGTH_LONG).show();
}
PS: I HAVE TO make this method.invoke() work. Otherwise, I need to write a very long list of switch-case statements.I've gone through the documentation but couldn't understand much about the object instance i might need to use here as I'm new to android app developing.
You can try something similar to the code shown below (Java Reflection) -
Suppose I have a class ClassWithMethods.java with the methods I want to invoke in some other class as shown below -
public class ClassWithMethods {
private int counter;
public void printIt(){
System.out.println("printIt() no param");
}
public void printItString(String temp){
System.out.println("printIt() with param String : " + temp);
}
}
Now I also have another class TestApp.java which will invoke methods of the ClassWithMethods class at runtime using Java Reflection -
public class TestApp {
public static void main(String[] args) {
//no paramater
Class noparams[] = {};
//String parameter
Class[] paramString = new Class[1];
paramString[0] = String.class;
//int parameter
Class[] paramInt = new Class[1];
paramInt[0] = Integer.TYPE;
try{
//load the ClassWithMethods at runtime
Class cls = Class.forName("com.myapps.ClassWithMethods");
Object obj = cls.newInstance();
//call the printIt method
Method method = cls.getDeclaredMethod("printIt", noparams);
method.invoke(obj, null);
//call the printItString method, pass a String param
method = cls.getDeclaredMethod("printItString", paramString);
method.invoke(obj, new String("someString"));
}catch(Exception ex){
ex.printStackTrace();
}
}
I am using Java Reflection in my current project (since you mentioned you are using Android Studio) to get Battery Capacity of device from PowerProfile class which is internal to the Android OS.
public double getBatteryCapacity() {
Object mPowerProfile = null;
try {
mPowerProfile = Class.forName("com.android.internal.os.PowerProfile")
.getConstructor(Context.class)
.newInstance(getContext());
} catch (Exception e) {
e.printStackTrace();
}
try {
// get access to method named "getAveragePower()" in the class "PowerProfile"
Method getAveragePower = Class.forName("com.android.internal.os.PowerProfile").getMethod("getAveragePower", String.class);
//Get total battery capacity in mAh.
double batteryCapacity = (Double) getAveragePower.invoke(mPowerProfile, "battery.capacity");
return batteryCapacity;
} catch (Exception e) {
e.printStackTrace();
}
return 0.0;
}
Here is a screenshot of how the actual method structure looks like in the PowerProfile class -

Java - Pass a subclass to a construstor typed to a superclass

I am trying to make a manager for a section of classes.
public Manager(int amount, Class<SuperObject> c) {
array = new SuberObject[amount];
for (SuperObject o : array) {
try {
o = c.newInstance();
} catch (Exception e) {
//Do stuffs
}
}
That works just fine but when I make the call:
Manager man = new Manager(5, SubClass);
I get a failure to compile because it is not expressly SuperClass. Any guidance would be helpful. Thanks!
Class<SuperObject> expects a SuperObject to be given, and only a SuperObject. Use Class<? extends SuperObject> instead.

Event handling with Java Reflection

Ok so, this is quite confusing to explain. I will try my best.
Inspired by the Bukkit Event System where you can make voids an event handler by just using #EventHandler.
Example:
#EventHandler
public void aRandomName(PlayerMoveEvent ev) {
}
As you can see, the name of the method doesn't matter. Which event is passed on is determined by the event argument type.
All events extend the Event class.
I have made up some code which I think would work, except for one thing.
public List<Object> eventContainers;
public void fireEvent(Event e) {
Method[] methods;
for (Object o : eventContainers) {
Object[] classes = o.getClass().getClasses();
for (Object clss : classes) {
methods = clss.getClass().getMethods();
for (Method m : methods) {
if (m.getAnnotation(EventHandler.class) != null) {
try {
Class[] requiredTypes = m.getParameterTypes();
for(Class cl : requiredTypes) {
if(e.equals(cl)) {
m.invoke(clss, e);
}
}
} catch (IllegalAccessException ex) {
} catch (IllegalArgumentException ex) {
} catch (InvocationTargetException ex) {
}
}
}
}
}
}
What my code does:
Loops through all the classes in eventContainers, looks for methods that have the #EventHandler annotation and sends the specified event to that method. However, I want to see what kind of event the given event in fireEvent(Event e) is, and then look at the methods who require an event parameter of that kind. How would I do that? I figure that
Class[] requiredTypes = m.getParameterTypes();
for(Class cl : requiredTypes) {
if(e.equals(cl)) {
m.invoke(clss, e);
}
}
will not work.
Ultimately I want to be able to pass on events to plugins. Like this:
EventManager.fireEvent(new PlayerMoveEvent(player));
Which will be sent to all plugins and the plugins that have
#EventHandler
public void aVoid(PlayerMoveEvent e) {
//stuff
}
If you have any questions, I will try to explain it better. Thanks in advance for your help!
Your code uses e.equals(cl), which is comparing an instance of Event with an instance of Class (the class of an instance of Event) - this will never return true. What you want to do instead is:
if(e.getClass().equals(cl)) {
m.invoke(clss, e);
}
Alternatively, if you want methods annotated with #EventHandler to handle all subclasses of the class that their method signature defines (i.e. a method like handle(Event e) would be called with PlayerMoveEvents as well as all other events), then you want:
if(cl.isAssignableFrom(e.getClass())) {
m.invoke(clss, e);
}
See the Class Javadoc here for more information.
Note that I think there are a few other problems in you code. For example, Method.invoke should be called with an instance of the class that contains a method that is annotated with #EventHandler. It is a little unclear from your code, but I believe this should therefore be:
m.invoke(o, e);
Also, by calling o.getClass().getClasses(), you are iterating over the classes defined in the class of o - you probably want to iterate over the methods of the class of o directly, i.e.:
for (Method m : o.getClass().getMethods()) {
if (m.getAnnotation(EventHandler.class) != null) {
Class[] requiredTypes = m.getParameterTypes();
if (requiredTypes.length == 1 && requiredTypes[0].isAssignableFrom(e.getClass()) {
m.invoke(o, e);
}
}
}
You can get the parameter types from a Method using method.getGenericParameterTypes(), so:
m.invoke(clss, m.getGenericParameterTypes()[0].class.cast(e));
Not sure if that's what you want.
Assuming the EventHandler annotated method only has one parameter
Method[] methods = YourClass.class.getDeclaredMethods();
Object yourInstance = null; // get it
Event e = null; // get it
for (Method method : methods) {
EventHandler handler = method.getAnnotation(EventHandler.class);
if (handler != null) {
Class<?>[] parameterTypes = method.getParameterTypes();
// you're going to need different logic if you have more than one parameter
if (parameterTypes.length == 1 && parameterTypes[0].isAssignableFrom(e.getClass())) {
method.invoke(yourInstance, e);
}
}
}
I've not included any exception handling.
Get all the methods of event handler candidate classes and iterate over them. If a method has the #EventHandler annotation, get its parameter type list. If it only has one parameter and that type is assignable from your event type e.getClass(), then invoke it passing in your event.
I have now modified the code to a working event system!!!!!! :D thanks so much andersschuller!
public void fireEvent(Event e) {
Method[] methods;
for (Object o : eventContainers) {
methods = o.getClass().getMethods();
for (Method m : methods) {
if (m.getAnnotation(EventHandler.class) != null) {
try {
if (m.getParameterTypes()[0].isAssignableFrom(e.getClass())) {
m.invoke(o, e);
}
} catch (IllegalAccessException ex) {
} catch (IllegalArgumentException ex) {
} catch (InvocationTargetException ex) {
}
}
}
}
}
I kept all answers in mind, thanks all!

Categories

Resources