How to resolve incorrect parameters from LambdaMetafactory - java

I'm trying to execute annotated method within main method in App.java with LambdaMetafactory.metafactory():
import com.drfits.annotation.RunMethod;
import com.drfits.transfer.Transfer;
import com.drfits.transfer.TransferExecutor;
import com.drfits.transfer.TransferExecutorImpl;
import com.drfits.transfer.TransferImpl;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.function.Function;
/**
* Application for execute annotated method
*/
public class App {
public static void main(String[] args) {
Transfer transfer = new TransferImpl("Hello, World!");
Method[] methods = TransferExecutorImpl.class.getMethods();
for (Method method : methods) {
RunMethod annotation = method.getAnnotation(RunMethod.class);
if (annotation != null) {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle methodHandle = lookup.unreflect(method);
MethodType invokedType = MethodType.methodType(Function.class);
MethodType functionMethodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
// Lambda which can be executed
TransferExecutor transferExecutor= new TransferExecutorImpl();
Function<Transfer, Void> commonLambda = transferExecutor::execute;
commonLambda.apply(transfer);
// Lambda constructed manually
Function<Transfer, Void> constructedLambda = (Function) LambdaMetafactory.metafactory(
lookup,
"apply",
invokedType,
functionMethodType,
methodHandle,
methodHandle.type()).getTarget().invokeExact();
constructedLambda.apply(transfer);
} catch (Throwable t) {
System.out.println(t.getMessage());
}
}
}
}
}
If I'm trying to execute this code it'll throw exception:
Incorrect number of parameters for instance method invokeVirtual com.drfits.transfer.TransferExecutorImpl.execute:(Transfer)void; 0 captured parameters, 1 functional interface method parameters, 1 implementation parameters

Using the code
TransferExecutor transferExecutor= new TransferExecutorImpl();
Function<Transfer, Void> commonLambda = transferExecutor::execute;
you are binding the Function to a particular instance of TransferExecutor. Your dynamic creation code lacks an instance for the invocation of the instance method TransferExecutorImpl.execute. That’s what the exception tries to tell you.
An instance method needs a target instance to be invoked on, hence your target method has a functional signature of (TransferExecutor,Transfer)→Void.
You can either, create a BiFunction<TransferExecutor,Transfer, Void> out of this method or bind an instance to it like with your transferExecutor::execute method reference. For the latter
change the invoked type to receive an instance of TransferExecutor
MethodType invokedType = MethodType.methodType(
Function.class, TransferExecutorImpl.class);
provide the argument at the invocation:
… .getTarget().invokeExact((TransferExecutorImpl)transferExecutor);
Note that there is still a subtle difference. The statement Function<Transfer, Void> commonLambda = transferExecutor::execute; refers to the interface method while the method you have identified via your annotation is the method declared in TransferExecutorImpl.
Regarding binding captured values, see this and that answer for more explanation and examples.

The error is trying to tell you are not passing another parameters. It expects one, but you are not passing it one.
I suggest you look at the parameters you are passing and compare them to the parameters actually passed when building a lambda.
Write what you are trying to do and a lambda first and see what parameters you should be passing.

Related

How to get lambda expression from Java Method

Suppose that I have the following method:
void test () {...}
I am getting this method via reflection, but invoking it will be very slow, so I want to get Runnable from it as if I will write
this::test
Is there any way to achieve this?
I need an implementation for method like this: Runnable toRunnable(Method method); So we are getting a Method and we need to return Runnable
This is not exactly equivalent to this::test, since that also uses the this instance to bind to, so you will also have to pass an instance to bind to. But then you can use method handles, which is the underlying implementation for something like this::test.
With a class like this:
public class MyClass {
public void test() {
System.out.println("Test Called");
}
}
You can create this method:
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
public static Runnable toRunnable(Method method, Object instance) throws ReflectiveOperationException {
Lookup lookup = lookup();
MethodHandle test = lookup.unreflect(method);
try {
return (Runnable) LambdaMetafactory.metafactory(
lookup,
"run",
methodType(Runnable.class, instance.getClass()),
methodType(void.class),
test,
methodType(void.class)
).getTarget().invoke(instance);
} catch (Throwable e) {
throw new RuntimeException("Should not occur", e);
}
}
And call it like this:
Object ref = new MyClass(); // get from somewhere
Runnable result = toRunnable(ref.getClass().getMethod("test"), ref);
result.run(); // prints 'Test Called'
The caveat is that the test method has to be accessible from the point at which you're calling lookup(), you can get around this either by passing the Lookup to the method manually, and creating it at a place where you can access the test method. Or if you're in Java 9 you can use privateLookup(Class<?>, Lookup) instead, but the Lookup you pass to that needs to be created in the same module as the method you're trying to access. (in short, method handles has a few more access restrictions to it). But if the method and class you're trying to access are publicly accessible then there's no problem.

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.

Can Lambda expressions access private methods of classes outside their scope?

I want to gain reflective access to java.lang.String's package private constructor.
Namely, this one:
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
Creating a MethodHandle for it is simple enough, and so is invoking it.
The same is true for using Reflection directly.
But I'm curious whether it's possible to directly call the constructor via functional interfaces.
27602758 touches on a somewhat similar issue, but the solutions provided do not appear to work in this case.
The test case below compiles without issues. Everything works, except for the actual interface invocation.
package test;
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
public class Test {
// Creates a new String that shares the supplied char[]
private static interface StringCreator {
public String create(char[] value, boolean shared);
}
// Creates a new conventional String
private static String create(char[] value, boolean shared) {
return String.valueOf(value);
}
public static void main(String[] args) throws Throwable {
// Reflectively generate a TRUSTED Lookup for the calling class
Lookup caller = MethodHandles.lookup();
Field modes = Lookup.class.getDeclaredField("allowedModes");
modes.setAccessible(true);
modes.setInt(caller, -1); // -1 == Lookup.TRUSTED
// create handle for #create()
MethodHandle conventional = caller.findStatic(
Test.class, "create", MethodType.methodType(String.class, char[].class, boolean.class)
);
StringCreator normal = getStringCreator(caller, conventional);
System.out.println(
normal.create("foo".toCharArray(), true)
// prints "foo"
);
// create handle for shared String constructor
MethodHandle constructor = caller.findConstructor(
String.class, MethodType.methodType(void.class, char[].class, boolean.class)
);
// test directly if the construcor is correctly accessed
char[] chars = "foo".toCharArray();
String s = (String) constructor.invokeExact(chars, true);
chars[0] = 'b'; // modify array contents
chars[1] = 'a';
chars[2] = 'r';
System.out.println(
s
// prints "bar"
);
// generate interface for constructor
StringCreator shared = getStringCreator(caller, constructor);
System.out.println(
shared.create("foo".toCharArray(), true)
// throws error
);
}
// returns a StringCreator instance
private static StringCreator getStringCreator(Lookup caller, MethodHandle handle) throws Throwable {
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"create",
MethodType.methodType(StringCreator.class),
handle.type(),
handle,
handle.type()
);
return (StringCreator) callSite.getTarget().invokeExact();
}
}
Specficially the instruction
shared.create("foo".toCharArray(), true)
throws the following error:
Exception in thread "main" java.lang.IllegalAccessError: tried to access method java.lang.String.<init>([CZ)V from class test.Test$$Lambda$2/989110044 at test.Test.main(Test.java:59)
Why is this error still being thrown, despite access ostensibly being granted?
Can anyone come up with an explanation for why the generated interface has no access to a method that all of its components have access to?
Is there a solution or a viable alternative that actually works for this particular use case, without reverting to pure Reflection or MethodHandles?
Because I'm stumped.
The problem is that you override the lookup object to be trusted, so its access to a private method of String will pass the lookup procedure and lambda meta factory, but its still bound to your Test class as that’s the class which created the lookup object via MethodHandles.lookup() and the generated class will live in the same context. The JVM is quite generous regarding accessibility when it comes to these generated classes but apparently, accessing a private member of the bootstrap class java.lang.String from a class living in the context of your application class is not accepted.
You can get a lookup object living in an appropriate context via, e.g. MethodHandles.lookup() .in(String.class) (and then patching it to have private or “trusted” access), but then, you will get another problem: a class living in the context of java.lang.String (or just in the bootstrap loader’s context) will not have access to your custom interface StringCreator and can’t implement it.
The only solution is to use a lookup object living in the context of String and implementing one of the existing generic interfaces, accessible from the bootstrap class loader:
import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.util.function.BiFunction;
public class Test {
public static void main(String[] args) throws Throwable {
// Reflectively generate a TRUSTED Lookup for the String class
Lookup caller = MethodHandles.lookup().in(String.class);
Field modes = Lookup.class.getDeclaredField("allowedModes");
modes.setAccessible(true);
modes.setInt(caller, -1); // -1 == Lookup.TRUSTED
// create handle for shared String constructor
MethodHandle constructor = caller.findConstructor(
String.class, MethodType.methodType(void.class, char[].class, boolean.class)
);
// generate interface implementation for constructor
BiFunction<char[],Boolean,String> shared=getStringCreator(caller, constructor);
// test if the construcor is correctly accessed
char[] chars = "foo".toCharArray();
String s = shared.apply(chars, true);
chars[0] = 'b'; chars[1] = 'a'; chars[2] = 'r';// modify array contents
System.out.println(s); // prints "bar"
chars[0] = '1'; chars[1] = '2'; chars[2] = '3';
System.out.println(s); // prints "123"
}
private static BiFunction<char[],Boolean,String> getStringCreator(
Lookup caller, MethodHandle handle) throws Throwable {
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"apply",
MethodType.methodType(BiFunction.class),
handle.type().generic(),
handle,
handle.type()
);
return (BiFunction) callSite.getTarget().invokeExact();
}
}

Unit testing with Mockitos not working as expected

I am new with Mockito and I wanted to write a unit test for some legacy code. I did try few techniques mentioned for using mockito while testing my specific scenario but I am not able to find a solution to my usecase. Here is my scenario :
I have a method A( does local processing of data) and a method B (does some remote processing of data). Method A in turn calls my method B. I want to call method A and when it in turn calls method B, then I want to return a predefined value from method B, lets says a list of files. Here is the sample code from Test class and actual class:
Myclass mc = Mockito.mock(MyClass.class);
when(mc.methodB(param1,param2)).thenReturn(fileSet); // returns mocked fileSet (set of files)
when(mc.methodA(param1,param2)).thenCallRealMethod(); //calls real method A
mc.methodA(param1,param2); //does not return the mocked value from methodB
class MyClass{
public Set<File> methodB(param1,param2){
//some processing
return fileSet;
}
public void methodA(param1,param2){
//some processing
Set<FileSet> fileSet = methodB(param1,param2);
//some more processing on returned files
}
}
I created a mock of my class and made sure that when I invoke method A, real method call for method A happens and when I invoke method B, mock results are returned.
If I test method A and method B separately it works but when I call method A which in turn calls method B, it does not return my mocked value from method B.
Is this not the right way of doing this call or am I missing something?
If I understand correctly, you want to mock one method from a class while really calling another method from the same class.
This can be done using spies: with spies, every method from the object is really called, unless it was explicitely stubbed:
MyClass myClass = new MyClass();
MyClass spy = spy(myClass); // creates a spy for myClass
doReturn(new HashSet<File>() {{ add(new File("file1")); }}).when(spy).methodB("", ""); // stubbing method B
spy.methodA(param1, param2); // calling the real method A
Note that spies should be used carefully, only to deal with legacy code that you cannot change.
Note that the construct to stub methodB differs from the "usual" construct. If we write the following code:
when(spy.methodB("", "")).thenReturn(...);
what is happening is that the real methodB is getting actually called right here, and this is something we do not want. In the second construct:
doReturn(...).when(spy).methodB("", "");
the methodB is not called.
Here's a complete test class demonstrating spies:
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
public class TestClass {
#Test
public void test() {
MyClass myClass = new MyClass();
MyClass spy = spy(myClass); // creates a spy for myClass
doReturn(new HashSet<File>() {{ add(new File("file1")); }}).when(spy).methodB("", ""); // stubbing method B
spy.methodA("", ""); // calling the real method A
}
}
class MyClass {
public Set<File> methodB(String param1, String param2) {
return new HashSet<File>() {{ add(new File("file2")); }};
}
public void methodA(String param1, String param2) {
Set<File> fileSet = methodB(param1, param2);
System.out.println(fileSet); // prints [file1] and not [file2], meaning the stubbed method was called and not the real one
}
}

Unexpected Class.getMethod behaviour

A while ago I had a similar question when using Class.getMethod and autoboxing, and it made sense to implement this in your own lookup algorithm. But what really confused me a little was that the following is not working either:
public class TestClass
{
public String doSomething(Serializable s)
{
return s.toString();
}
public static void main(String[] args) throws SecurityException, NoSuchMethodException
{
TestClass tc = new TestClass();
Method m = tc.getClass().getMethod("doSomething", String.class);
}
}
String.class implements the Serializable interface and I really expected it to be included in the lookup method. Do I have to consider this in my own lookup algorithms as well?
EDIT: I did read the Javadoc, so let me emphasise the second part of the question: And if so do you have suggestions on how to do that fast (I already had to add some custom matching and converting algorithms and I don't want it to get too slow)?
As per your edit, you can make use of Class#isAssignableFrom(). Here's a basic kickoff example (leaving obvious (runtime) exception handling aside):
package com.stackoverflow.q2169497;
import java.io.Serializable;
import java.lang.reflect.Method;
public class Test {
public String doSomething(Serializable serializable) {
return serializable.toString();
}
public static void main(String[] args) throws Exception {
Test test = new Test();
for (Method method : test.getClass().getMethods()) {
if ("doSomething".equals(method.getName())) {
if (method.getParameterTypes()[0].isAssignableFrom(String.class)) {
System.out.println(method.invoke(test, "foo"));
}
}
}
}
}
This should print foo to stdout.
The javadoc for Class.getMethod is very explicit:
The parameterTypes parameter is an
array of Class objects that identify
the method's formal parameter types,
in declared order.
It offers no scope for subtypes.
getMethod isn't meant to find methods which are compatible with the given parameter types - it's meant to find methods with exactly the given parameter types.
You'd need to call getMethods() to find all the methods, then filter by name and number of parameters, then work out which of those are actually applicable.
Why would you call getMethod with String.class? Method signatures are exactly mapped. It doesn't make any sense to look up a method by the same criteria as if you will call them.

Categories

Resources