What is the easiest way to find and run private annotated method? - java

The following example runs MyClass#myMethod() only if it is public. It does not run it if it is private.
How to run even if private?
import org.apache.commons.lang3.reflect.MethodUtils;
import javax.annotation.PostConstruct;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Created by dims on 13.10.2016.
*/
public class CallPrivateMethodTest {
#Retention(RUNTIME)
#Target(METHOD)
public #interface MyAnnotation {
}
public static class MyClass {
#MyAnnotation
private void myMethod() {
System.out.println("myMethod() ran");
}
}
public static void main(String[] args) {
MyClass myObject = new MyClass();
List<Method> methods = MethodUtils.getMethodsListWithAnnotation(myObject.getClass(), MyAnnotation.class);
for(int i=0; i<methods.size(); ++i) {
try {
methods.get(i).invoke(myObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}

You have to call setAccessible(true) on your method.
See the Javadoc of AccessibleObject, which in turn is a supertype of Method.
In your example:
methods.get(i).setAccessible(true);
methods.get(i).invoke(myObject);
EDIT: As GhostCat pointed out in his answer, not all reflection calls return private methods, too. It seems that MethodUtils#getMethodsListWithAnnotation in fact does not return them.
To solve this problem, you'd have to fetch those methods by yourself:
MyClass myObject = new MyClass ();
Method[] allMethods = myObject.getClass ().getDeclaredMethods ();
List<Method> annotatedMethods = Arrays.stream (allMethods)
.filter (m -> m.getAnnotation (MyAnnotation.class) != null)
.collect (Collectors.toList ());
for (Method method: annotatedMethods) {
try {
method.setAccessible (true);
method.invoke (myObject);
} catch (IllegalAccessException e) {
e.printStackTrace ();
} catch (InvocationTargetException e) {
e.printStackTrace ();
}
}

Basically, you need to run a private method from the class itself. Maybe you should rethink your code ? Otherwise, Alex's answer will do the trick.
More informations on access levels : https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
And if you want to use Reflection, you can find even more infos about the behavior here : Java reflection - impact of setAccessible(true)

Actually, there can be two reasons why your code doesn't work:
That private method shows up in that list; but before calling it, you have to make it accessible (you already got an answer for that)
Depending on the implementation of that Util class you are using, maybe getMethodsListWithAnnotation returns a list that simply doesn't include any private methods? ( you see, the mechanism in java reflection to get a hold on private methods is different than for public methods. thus it wouldnt surprise me if that util class is making the same distinction).
And in any case: private methods are private for a reason. They represent internal implementation details. They are subject to be changed all the time.
In that sense: your real problem is that you think there is a valid reason to invoke a private method using reflection. You should work on that first. If you really think that method should be annotated; and be called via reflection; then at least make it public.

Related

Java - How to invoke a method from another class, with the class name and method name as a string

This method is called regularly
public static void stynax(String N[]) {
if (N[1].equals("echo")) { echo.s(); main(); }
if (N[1].equals("detectos")) { detectos.s(); main(); }
if (N[1].equals("getuser")) { getuser.s(); main(); }
if (N[1].equals("exit")) { exit.s(); main(); }
if (N[1].equals("makefile")) { makefile.s(); main(); }
if (N[1].equals("cd")) { cd.s(); main(); }
if (N[1].equals("system")) { system.s(); main(); }
main();
}
How can I invoke all these methods
system.s();
echo.s();
Ect, by seeing if the class exists, then calling the corresponding method.
N[1] is always the class name. The class where this method is stored is in a class called main, and the classes that are called are in a different package called Commands.
I always seem to get this error, when trying to make a Class variable, i think this is the main issue.
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
So it never gets to invoke the method.
To simplify.
1) The program gets the class name as a String as N[1]
2) It sees if the class exists
3) If the class exists it calls it by the name of the class N[1].s();
Edit: Imports used
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import cgameing.Commands.FileBrowser;
import cgameing.Commands.banner;
import cgameing.Commands.cd;
import cgameing.Commands.detectos;
import cgameing.Commands.echo;
import cgameing.Commands.exit;
import cgameing.Commands.getuser;
import cgameing.Commands.makefile;
import cgameing.Commands.system;
end of edit:
This one works, for anyone wanting to do the same thing
(Class.forName("commands."+N[1])).getDeclaredMethod("s", null).invoke(null,null);
Thanks everyone
You'll need to use reflection. Try something as follows. Use fully qualified class name instead of "XYZ" if your class is in a different package.
import java.lang.reflect.*;
import java.lang.*;
public class ReflectionTest {
public static void main(String[] args)throws NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InvocationTargetException {
(Class.forName("XYZ")).getDeclaredMethod("ABC", null).invoke(null,null);
}
}
class XYZ
{
public static void ABC()
{
System.out.println("Lulz");
}
}
For your use case given your classes are in commands package (as you stated in a comment). The fully qualified name will then be commands.classname
(Class.forName("commands."+N[1])).getDeclaredMethod("s", null).invoke(null,null);
You can use Reflection.
You have Class name coming in Array.
You can use "Class" and "Method" class. Class can determine if the class exists or not, and method can be used to call method you need to call.
try {
Class<?> c = Class.forName(N[1]);
Object t = c.newInstance();
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
// if name matches use invoke method.
}
} catch (ClassNotFoundException x) {
//handle exception
}
Please consult API if you need to see more details.
I recommend you avoid using reflection if you possibly can. Better is to define the commands you expect to see - ideally in an enum.
For example:
enum Command {
CD(FileSystem::cd),
EXIT(Application::exit),
MAKEFILE(FileSystem::createFile),
...
private final Runnable runnable;
Command(Runnable runnable) {
this.runnable = runnable;
}
public void run() {
runnable.run();
}
}
You can still use the name to get the command if you wish (automatically throwing an exception if the value isn't found in the enum - which is presumably what you would want):
Command.valueOf(commandString.toUpperCase()).run();
Or call the commands directly without having to know which method they delegate to:
Command.MAKEFILE.run();
Given you are going to have a list of if statements somewhere, you might as well encapsulate that in an enum which is much more explicit than embedding the method names.
Okay, everyone seems to suggest reflection, there is at least one alternative way to do it, depending on whether you know your class and method names at compile time or not.
So, lets say we have this method in someclass:
public void changeDirectory(String args) {
//do something
}
If they are known, you could easily use method references:
HashMap<String, Consumer<String[]>> commands = new HashMap<>();
commands.put("cd", SomeClass::changeDirectory);
commands.get("cd").accept(args);
The drawback would be, the method signature would have to be the same... on the other hand, if you know the exact method anyway, you could just use a switch statement and call them directly...
If you want to do it dynamically, the alternative to reflection would be MethodHandles. The advantage is that they would only check access once on creation and have some other optimizations that should make them faster than reflection in most cases.
Class<?> dynamicallyFoundClass = Class.forName("some.package.SomeClass");
Class<?> fixedClass = SomeClass.class;
String methodName = "changeDirectory";
MethodType type = MethodType.methodType(void.class, String.class);
handles.put("cd", lookUp.findStatic(dynamicallyFoundClass, methodName, type));
handles.get("cd").invoke(args);
But how complicated an approach you have to use would depend on what you know at compile time and what has to be found at runtime.

AspectJ annotation on field that triggers #Before advice

I have already written AspectJ aspects that perform #Around advice triggered by method annotations. Now I want to do the same, but where fields are annotated instead of methods. So with each method invocation of the class below, it must set the accountSummary field to the correct implementation. Is there a way to accomplish this? I presume using #Before advice would be the best way of going about it. Using CDI is not an option - the solution must use AspectJ.
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacy=LegacyAccountSummary.class,new=NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
// use correct accountSummary impl, decided in #Before code
}
public void bar() {
// use correct accountSummary impl, decided in #Before code
}
}
I am not sure what exactly you want to achieve, so I am presenting two alternative solutions.
First, let us create some application classes in order to have a fully compileable sample:
package de.scrum_master.app;
public interface AccountSummary {
void doSomething();
}
package de.scrum_master.app;
public class LegacyAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
public class NewAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectServiceClientAdapter {
Class<?> legacyImpl();
Class<?> newImpl();
}
package de.scrum_master.app;
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacyImpl = LegacyAccountSummary.class, newImpl = NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
accountSummary.doSomething();
}
public void bar() {
System.out.println("Account summary is " + accountSummary);
}
}
Now we need an entry point:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
PoolableBusinessLogic businessLogic = new PoolableBusinessLogic();
businessLogic.foo();
businessLogic.bar();
System.out.println();
}
}
}
Obviously this yields an error because the member accountSummary has not been initialised:
Exception in thread "main" java.lang.NullPointerException
at de.scrum_master.app.PoolableBusinessLogic.foo(PoolableBusinessLogic.java:8)
at de.scrum_master.app.Application.main(Application.java:7)
Now we have two options, depending on what you want to achieve:
Option A: dynamic injection
Scenario: For each field access (even in the same PoolableBusinessLogic instance) decide dynamically what type of object instance to return. Here in this example I will just be randomising in order to simulate another if-else criterion.
BTW, I hope it is okay that I use the more expressive native AspectJ syntax. You can easily convert the aspect to annotation style.
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect DynamicInjectionAspect {
private static final Random RANDOM = new Random();
Object around(InjectServiceClientAdapter adapterAnn) :
get(* *) && #annotation(adapterAnn)
{
try {
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
return implClass.newInstance();
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This yields the following output:
I am de.scrum_master.app.LegacyAccountSummary#4d9cfefb
Account summary is de.scrum_master.app.NewAccountSummary#7e28388b
I am de.scrum_master.app.NewAccountSummary#2986e62
Account summary is de.scrum_master.app.LegacyAccountSummary#6576e542
I am de.scrum_master.app.NewAccountSummary#60c58418
Account summary is de.scrum_master.app.LegacyAccountSummary#4763754a
I am de.scrum_master.app.NewAccountSummary#52a971e3
Account summary is de.scrum_master.app.NewAccountSummary#7274187a
I am de.scrum_master.app.LegacyAccountSummary#23f32c4a
Account summary is de.scrum_master.app.LegacyAccountSummary#31e0c0b6
As you can see, within each of the five output groups (i.e. for each PoolableBusinessLogic instance) there are different account summary object IDs and sometimes (not always) even different class names.
Option B: static injection
Scenario: Per PoolableBusinessLogic instance decide dynamically what type of object instance to statically assign to the annotated member if its value is null. After that, do not overwrite the member anymore but return the previously initialised value. Again I will just be randomising in order to simulate another if-else criterion.
Attention: Do not forget to deactivate the first aspect, e.g. by prepending if(false) && to its pointcut. Otherwise the two aspects will be conflicting with each other.
package de.scrum_master.aspect;
import java.lang.reflect.Field;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect StaticInjectionAspect {
private static final Random RANDOM = new Random();
before(InjectServiceClientAdapter adapterAnn, Object targetObj) :
get(* *) && #annotation(adapterAnn) && target(targetObj)
{
try {
Field field = targetObj.getClass().getDeclaredField(thisJoinPoint.getSignature().getName());
field.setAccessible(true);
if (field.get(targetObj) != null)
return;
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
field.set(targetObj,implClass.newInstance());
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This is a bit uglier because it involves using reflection for finding the member field. Because it might be (and in our example really is) private we need to make it accessible before doing anything with it.
This yields the following output:
I am de.scrum_master.app.NewAccountSummary#20d1fa4
Account summary is de.scrum_master.app.NewAccountSummary#20d1fa4
I am de.scrum_master.app.NewAccountSummary#2b984909
Account summary is de.scrum_master.app.NewAccountSummary#2b984909
I am de.scrum_master.app.LegacyAccountSummary#1ae3043b
Account summary is de.scrum_master.app.LegacyAccountSummary#1ae3043b
I am de.scrum_master.app.LegacyAccountSummary#2e2acb47
Account summary is de.scrum_master.app.LegacyAccountSummary#2e2acb47
I am de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Account summary is de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Now the output looks different: Within each of the five output groups (i.e. for each PoolableBusinessLogic instance) both output lines show exactly the same object ID.
For Option A: dynamic injection in kriegaex's answer, the annotation-style aspect will look like this:
#Aspect
public class InjectServiceClientAdapterAspect {
#Pointcut("get(* *) && #annotation(injectAnnotation)")
public void getServiceClientAdapter(InjectServiceClientAdapter injectAnnotation) {
}
#Around("getServiceClientAdapter(injectAnnotation)")
public Object injectServiceClientAdapter(final ProceedingJoinPoint joinPoint, final InjectServiceClientAdapter injectAnnotation) {
// injection code goes here
}

Java - How to create a Class<Map<Object,List<Object>>> object

I'm sorry if this is a noob question. I have a method which takes a Class object with the generic type as a parameter. I am able to do the following:
Class cs = Map.class;
but when I pass this I get an "NoSuchMethodException" since the method is declared:
public void doSomething(Class<Map<Object, List<Object>>> theclass){
...
}
I have tried to cast it like this:
Class cs = (Class<Map<Object, List<Object>>>)(Class<?>)Map.class;
but I still get the same exception.
Is there a way to do this?
I cannot post the full code here but I have reproduced the error:
The application is actually using reflection. I can not post the original code here but I have reproduced the exception:
import java.lang.reflect.Method;
import java.util.*;
public class Main {
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("Second");
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
String m = "doSomething";
Method method = null;
Class cs = Map.class;
try{
method = c.getMethod(m, cs);
} catch (NoSuchMethodException e){
e.printStackTrace();
}
}
}
class Second{
public static void doSomething(Class<Map<Object, List<Object>>> theclass){
System.out.println("HERE I AM!");
}
}
The exception:
java.lang.NoSuchMethodException: Second.doSomething(java.util.Map)
at java.lang.Class.throwNoSuchMethodException(Class.java:283)
at java.lang.Class.getMethod(Class.java:825)
at Main.main(Main.java:16)
If you're doing such casting, I think you're fighting against the type system that's designed to help you. For starters,
Class cs = Map.class;
is a raw type (see here for more info). Have you tried instantiating a class of your required Map type, and passing the class of that to your function ?
e.g.
(new HashMap<Object, List<Object>>()).getClass();
Note: as ruakh as noted in the comments above, you'd have to pass an instance of the Map interface, and as such that method declaration above would prohibit this. Basically there's a fundamental API design issue here!
Got it! I have changed the following line:
method = c.getMethod(m, cs);
to:
method = c.getMethod(m, cs.getClass());
Now it works!

Java. How can I upcast type use reflection?

I have a two classes UpObj and DownObj
public class UpObj {
public UpObj() {
System.out.println("Load UpObj ");
}
}
public class DownObj extends UpObj {
public DownObj() {
System.out.println("Load DownObj ");
}
}
public class Caller {
UpObj obj;
public Caller(UpObj obj) {
this.obj = obj;
System.out.println("!!!");
}
}
public class GNUMakeFile {
/**
* #param args
*/
public static void main(String[] args) {
DownObj iView = new DownObj();
Class<?> iViewClass = iView.getClass();
Class<?> clazz;
try {
clazz = Class.forName("bla.bla.bla.Caller");
Constructor<?> ctor = clazz.getDeclaredConstructor(iViewClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
So, I want upcast my child type DownObj to parent UpObj in Caller constructor.
I think this is possible with help generics. Something like this .
Anybody know how exactly this use.
Thanks.
You shouldn't really be using Class.getConstructor() with the runtime type of what you want to pass to it – the method works off the formal parameters.
If you need to search for a constructor that matches some objects that you have, you'll have to loop over getConstructors(), inspect the formal parameters of each constructor, and check whether you found a suitable signature using Class.isAssignableFrom(). I don't think there's a convenient way to have the reflection API do overload resolution for you.
(Alternatively, rethink your approach so messing with reflection is no longer necessary.)
There is no standard way doing it. Try to google for some solutions, e.g. http://www.xinotes.org/notes/note/1329/ or How to get parameter types using reflection? . As I remember the Spring and some other libraries have similar util functions to do it. Do you use the Spring?
I didn't really understand the problem - upcasting is normally not needed.
but it seams like the problem is to get the upper class of the object,
if use the getSuperclass method:
Constructor<?> ctor = clazz.getDeclaredConstructor(iViewClass.getSuperclass());
Documentation: getSuperclass()

Java: Only allow instantiation by one class

I want certain classes in my project to be pooled. And so I don't want to be able to instantiate these classes using: new SomeClass(), but instead obtain a new item from the pool using SomeClass.allocate().
I have this kind of code for each class that needs pooling.
public class GameObject
{
// Pooling: Provides a static method for allocation and a method for freeing
private static Pool<GameObject> pool = new Pool<GameObject>();
public static GameObject allocate() { return pool.obtain(); }
public void free() { pool.free(this); }
...
}
Now I can disable the normal way of instantiating by making the default constructor private, but the problem is that the pool needs to instantiate the class when it's created, and also when the pool needs to expand.
Is there some way to limit construction to only by the pool?
You have 2 options I can see: either make it an inner-class of the pool or make the allocate method package-private and put it in the same package as the pool.
EDIT: Ah. Just make the constructor private and then override whatever method the Pool uses to create new instances. As a (crude) example using your frame above:
public abstract class Pool<T>
{
public abstract T getNewObject();
public T obtain(){ return getNewObject(); }
public void free(T obj) {}
}
and
public class GameObject
{
// Pooling: Provides a static method for allocation and a method for freeing
private static Pool<GameObject> pool = new Pool<GameObject>(){
public GameObject getNewObject(){ return new GameObject(); }
};
public static GameObject allocate() { return pool.obtain(); }
private GameObject(){}
public void free() { pool.free(this); }
}
GameObject's constructor is happily inaccessible to anyone else.
As a last resort,you are able to use reflection. For other option,other people already tells.
I remember Spring container is able to init class that has private constructor. And I am surprised of that. I guess it also uses this trick. The benefit could be it is more generic.
public static void main(String... args) {
try {
Constructor c = GameObject.class.getDeclaredConstructor();
c.setAccessible(true); // solution
c.newInstance();
// production code should handle these exceptions more gracefully
} catch (InvocationTargetException x) {
x.printStackTrace();
} catch (NoSuchMethodException x) {
x.printStackTrace();
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
}
}
Alterative: Don't try to make your code jump through hoops. Use static analysis to enforce rules like this. The tools will catch it if you accidentally do something that you didn't intend to.

Categories

Resources