I have been trying to develop an application. A bean script will be written as per requirement which in turn will call methods (defined in the application) in various order as per requirement. The application code (apart for bean script) would not be changed.
Also, the application uses external jars which provide large number of methods - of which some are implemented in the application. However, I would like to have the possibility to use the other methods (ones that are not yet implemented) without making changes to application should the requirement arise. For this, I would like to use the Java reflection API. The user should be able to call any method present in the external jars by passing the method name and corresponding parameters (using the the external jar documentation).
I'm a java newbie so I have some code that tries to achieve it (may not be syntactically correct):
public void callExternalJarMethod(String methodName, Class[] methodParameterTypes, Object[] methodParameters)
throws NoSuchMethodException {
String className = "SampleClassName";
Class classObject = Class.forName(className);
Method methodObject;
if (methodParameterTypes.length == 0) {
methodObject = classObject.getMethod(methodName, null);
} else {
methodObject = classObject.getMethod(methodName, methodParameterTypes);
}
// Not handling calling of static methods in which case "null" would be passed instead of methodObject to the invoke method
Object returnValue = methodObject.invoke(methodObject, methodParameters);
}
I'm trying to find a way I can get the Class[] methodParameterTypes, and Object[] methodParameters populated with the relevant values. I would have the parameter types and parameter values as string. Also, any pointers towards useful utils would be appreciated.
Thanks in advance.
You are not passing an instance of SampleClassName to the Method.invoke() call here...
Object returnValue = methodObject.invoke(methodObject, methodParameters);
If the method you are going to invoke is static, you can do this...
Object returnValue = methodObject.invoke(null, methodParameters);
Otherwise (non-static), you need to create an instance of SampleClassName to execute the method on.
If the class does not need any constructor arguments, you could use...
Object returnValue = methodObject.invoke(classObject.newInstance(), methodParameters);
(Obviously there will be a load of Exceptions that you need to handle by doing "newInstance" and "invoke"...)
Related
I know I can get the method and classname from StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); but that is not what I want.
I want the class object, so I can access his interface, annotations, etc...
It is possible?
Class<?> classObject = getCallerClass();
I see this question, but that is just for the classname.
How to get the caller class in Java
Edit: Now I'm passing the class this way:
someService.dummyMethod(foo1, foo2, new Object(){}.getClass());
someService(String foo1, int foo2, Class<?> c) {
// stuff here to get the methodname,
// the interface of the class and an annotation from the interface.
}
I call someService from a lot of different classes, If is not possible I will continue this way, but If there is a way to get the caller class at runtime I prefer that way.
If you're using Java 9+ you can use java.lang.StackWalker.
public void foo() {
Class<?> caller = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.getCallerClass();
}
However, since StackWalker is thread safe it might be beneficial to create an instance and store it somewhere (rather than create a new instance every time the method is called).
Javadoc of getCallerClass():
Gets the Class object of the caller who invoked the method that
invoked getCallerClass.
This method filters reflection frames, MethodHandle, and hidden frames
regardless of the SHOW_REFLECT_FRAMES and SHOW_HIDDEN_FRAMES options
this StackWalker has been configured with.
This method should be called when a caller frame is present. If it is
called from the bottom most frame on the stack, IllegalCallerException
will be thrown.
This method throws UnsupportedOperationException if this StackWalker
is not configured with the RETAIN_CLASS_REFERENCE option.
Get the classname using the code of your linked question: How to get the caller class in Java
Then use the classname to retrieve the class, using code from here: Getting class by its name
Complete code:
String callerName = Thread.currentThread().getStackTrace()[2].getClassName();
try {
Class<?> caller = Class.forName(callerName);
// Do something with it ...
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
(community answer, since only mix of existing answers).
Using reflection in Java, I've written some code to iterate through an arraylist of the names of classes I want enabled, instantiate them and in turn invoke a method with the common name 'parse(String)' within each.
for (String parser : enabledParses)
{
Class<?> parserClass = Class.forname(String.format"%s.%s", parserDirectoryPath, parser));
Method parseMethod = parserClass.getMethod("parse", String.class);
Object parserObj = null;
switch (parser)
{
case "parser1" :
parserObj = parserClass.getConstructor(ConfigWrapper.class, Alerter.class).newInstance(config, alerter);
break;
...
...
...
}
parseMethod.invoke(pluginObj, stringToParse);
When testing, how can I check to see that the parse() method has been called for each of the enabled parsers?
Thanks!
If you want to verify, that some method was called, you can use Mockito and then Mockito.verify (....).
But in this case your test is to big, because your implementation is too big, try to make better decomposition. Split it to more method, which are simple.
For example:
List<MyInterface> loadAndCreateInstances(File ...)
and
void callParse(List<MyInterface> instances) {}
Then use Mockito and create List of mocked/spyed objects (Collections.singletonList(myMock)). Verification:
Mockito.verify(myMock,times(1)).parse(any())
Let me know, if I said all :-)
I want to do something different with java reflection. The program i written to add global listeners to java components when applets are opened from browser. An event fired and i get event source object. Here i don't know the actual class name that object referring to.
if(object.getClass.getName().contains("oracle.ewt.laf.basic.BasicTabBarUI$Menu"))
{
// here we can invoke methods,fields,etc using reflection
}
I can call the methods of BasicTabBarUI$Menu class with reflection.
Suppose now i have the following lines with me in the above if block
LWMenuItem menuItem = (LWMenuItem)object;
menuItem.getLabel());
I don't want to specify LWMenuItem class name , instead i want to call its method getLabel(). If we know the class name , we can do as above. But how can we do same with reflection. How can we do casting in reflection?
You don't need to do casting, except of the result of calling the method. Just use the object's Class object, which has a getMethod method that will return a Method object for the method you want, then invoke it:
Class cls = object.getClass();
Method getLabel = cls.getMethod("getLabel", null);
String label = (String)getLabel.invoke(object, null);
You can continue working with the basic object when using the return value from getLabel():
Method getLabelMethod = object.getClass().getMethod("getLabel");
Object menuItem = getLabelMethod.invoke(object);
menuItem.getClass().getMethod("getName").invoke(menuItem); // or whatever...
I have multiple modules with service interfaces binding to their corresponding types and I am able to get an instance by using
injector.getInstance(MyServiceInterface.class)
I would like to retrieve the instance using
injector.getInstance("MyServiceInterface")
i.e. a string literal instead of the class type
How can I achieve this ?
To elaborate my question further - I can retrieve the Class object from the string literal using a Class.forName(literal) call and then use it to retrieve the instance with a injector.getInstance(clsInstance) .
After retrieving the instance which I receive in my base service type interface I need to use reflection to invoke the method of the service object.
so Service serv = injector.getInstance(MyCustomService.class)
Now I need to invoke myCustomMethod() present in MyCustomService through reflection since this invoker is generic and is intended to work with multiple services without being aware of their actual type.
I will also need the Method interceptors configured on the service interfaces to be invoked transparently when I invoke the method on this instance reflectively.
While I'm not certain if there's functionality for that built into Guice itself, you could try getting the relevant Class<?> object yourself.
Something along the lines of:
Class<?> myServiceInterfaceClass = Class.forName("path.to.MyServiceInterface");
injector.getInstance(myServiceInterfaceClass);
This does however require that the current Classloader can access that specific class, etc.
This can't be done within Guice... because it can't be done, period! Think about it, let's say you have two of the same class name in different packages. Which class would you instantiate?
So at the very least the String would have to have the fully qualified class name, e.g. instead of Integer, it would have java.lang.Integer.
However, if you know which classes you want to support in advance, you can use a MapBinder.
Tweaking their example to match your use case:
public class ServiceModule extends AbstractModule {
protected void configure() {
MapBinder<String, MyServiceInterface> mapbinder
= MapBinder.newMapBinder(binder(), String.class, MyServiceInterface.class);
mapbinder.addBinding("MyServiceInterface").to(MyServiceImpl.class);
bind(MyServiceInterface.class).to(MyServiceImpl.class);
}
}
Now you can inject like this:
class ServiceManager {
#Inject
public ServiceManager(Map<String, MyServiceInterface> services) {
MyServiceInterface service = stacks.get("MyServiceInterface");
// etc.
}
}
Please note when you call inj.getInstance() you do have to know the return type of the Object you're trying to create, unless you are planning on doing:
Object foo = inj.getInstance(myString);
I'm having some trouble using reflection in Java. I'm attempting to save a method of a data structure but getting an error. The error is
java.lang.NoSuchMethodException: cs671.eval.SerialList.add(java.lang.Integer, java.lang.String)
The method, in this case, that I'm trying to get is the add method for a SerialList that takes a Comparable and an Object as its parameters.
structType = "cs671.eval.SerialList", keyType = "java.lang.Integer", and valType = "java.lang.String" are strings that were read in from a file.
Class dataClass = null, comparableClass = null, objectClass = null;
try{ // create data structure
dataClass = Class.forName(structType);
comparableClass = Class.forName(keyType);
objectClass = Class.forName(valType);
}
catch(ClassNotFoundException e){}
java.lang.Object structObj = null;
try{ // Create a data structure object
structObj = dataClass.newInstance();
}
catch(Exception e){}
Method m = null;
try{ // Attempt to get add method for the data structure
m = dataClass.getMethod("add", comparableClass, objectClass); // This is where it fails
}
catch(Exception e){}
Basically I'm trying to get the right method on the right datastructure with the correct classes that are going to get passed into that method but I don't know how to tell the getMethod method that those classes (comparableClass and objectClass) are the correct ones.
Thanks in advance!
Added: Here's the SerialList's add method signature
public void add(java.lang.Comparable, java.lang.Object)
You are saying -
The method, in this case, that I'm trying to get is the add method for a SerialList that takes a Comparable and an Object as its parameters.
But passing the classes - java.lang.Integer, java.lang.String.
Just a note - Only public methods are visible to getMethod() for non-publics you would have to use getDeclaredMethod() instead.
From http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#getMethod%28java.lang.String,%20java.lang.Class...%29:
To find a matching method in a class C: If C declares exactly one public method with the specified name and exactly the same formal parameter types, that is the method reflected. If more than one such method is found in C, and one of these methods has a return type that is more specific than any of the others, that method is reflected; otherwise one of the methods is chosen arbitrarily.
=> You need to pass java.lang.Comparable.class & java.lang.Object.class
Apologies for providing wrong answer earlier. Based on your comments it appears that you're trying to get a Method by avoiding to provide specific parameter types needed in the signature of that method.
If my understanding is correct then you should rather use Class#getMethods() and examine the returned Method[] for your method. Consider a skeleton code like this:
Method[] methods = dataClass.getMethods();
boolean matched = false;
// find a matching method by name
for (Method method: methods) {
Class<?>[] parameterTypes = method.getParameterTypes();
if ( "add".equals(method.getName()) && parameterTypes.length == 2 ) {
// method is your target method
// however you'll need more strict checks if there can be another add method
// in your class with 2 different parameter types
}
}
As other answers have stated, to use getMethod() you need to know and use the actual declared formal parameters of the method you are attempting to retrieve.
However, if for some reason you do not know the formal parameters at compile time, then you can iterate over all of the methods from the class until you find a method that fits your parameters (or find the most specific method that fits your parameters).
There is functionality written to do this already in apache commons bean utils, specifically in org.apache.commons.beanutils.MethodUtils.invokeMethod(...) and MethodUtils.getMatchingAccessibleMethod(...).
Source code for the above methods can be easily viewed online here.