I'm parsing a groovy script via Java using reflection to get the methods from the script.
For my project I need all the methods that are non-synthetic and public.
GroovyScriptEngine groovyScriptEngine = new GroovyScriptEngine(scriptFile.getAbsolutePath());
Class<GroovyObject> scriptClass = groovyScriptEngine.loadScriptByName(scriptFile.getName());
// some code
GroovyObject groovyObject = scriptClass.getConstructor().newInstance();
if (groovyObject != null) {
Method[] declaredMethods = groovyObject.getClass().getDeclaredMethods();
return Arrays.stream(declaredMethods)
.filter(m -> !m.isSynthetic() && Modifier.isPublic(m.getModifiers()))
.collect(Collectors.toList());
}
For a normal groovy class this returns all the getters and setter and public user-defined methods.
For example:
class MyGroovyClass {
def int number = 1
int add(int anotherNumber) {
return number + anotherNumber
}
}
Parsing the groovy class returns getNumber, setNumber, addNumber.
For an empty groovy class (no fields, no declared methods) this returns an empty list.
But for an empty script (= completely empty file) this returns the methods "run" and "main".
Why does getting and filtering (for non-synthetic and public) methods from an empty groovy script return "run" and "main"?
How can I filter for those two methods? (Or can I catch that case earlier?)
Well, groovy script has to be compiled into something that runs on JVM and has an entry point, apparently it only be a class with main method.
So basically groovy script gets compiled into a class, the body of the script is copied into run method, and main method has to be specified as an entry point as I've explained. At some point main will call run (indirectly).
Hence for each script you'll get these methods.
Its describe in groovy documentation (see paragraph 3.2. Script class)
Related
I have one interface method as follows and saved in Order.java file
and rhino script.
In rhino script I am trying to call the interface methods.
rhino:
var x = Packages.com.data.Order.X;
print(x);
java:
package com.data;
public interface Order
{
String X = "Hello, World!";
void invoke();
}
but it is not printing "Hello world".
Instead it is printing
uncaught JavaScript runtime exception: TypeError: Cannot call
property invoke in object [JavaPackage com.data.Order]. It is not a
function, it is "object". "
problem Statement: How to call java interface method from rhino script.
That type of error usually means that the Rhino engine is not seeing the class you are attempting to use.
If you are definitely seeing the jar get into the classpath, next, I'd double check that the jar contains your class exactly as referenced.
You can verify whether the JS step is seeing your class properly by Alerting it like this:
Alert(com.foo.bar.MyClass);
If the Alert indicates it is a JavaClass then it found your class. Otherwise it will say it is a JavaPackage.
Given a method defined on a (3rd party, so I can't just move it) Scala package object, like so:
package foo
package object bar {
def doSomething(s: String): String = ???
}
I need to call doSomething from Java code. I know that in general, I can get at a Scala companion object's methods from Java using ScalaObject$.method(). However, the example above compiles to foo.bar.package$.class, and of course Java screams about package being a reserved word.
Is there a way to call this from Java directly?
The best I can come up with (works, but ugly) is to wrap doSomething in Scala code that's not in a package object, and then call the wrapper from my Java code.
object BarUtil {
def wrapper(s: String) = foo.bar.doSomething(s)
}
and in Java
public String doIt(String s) {
return BarUtil$.wrapper(s);
}
You can access the package object as foo.bar.package$.MODULE$ (note the dollar signs, do not remove them):
foo.bar.package$.MODULE$.doSomething("hello")
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"...)
I'm aware that it is possible to use Java defined static methods in Lua, due to the section "Libraries of Java Functions" on http://luaj.org/luaj/README.html.
However I am struggling to find out how I can use the same for instance methods, I have a shortened example here:
private static class CallbackStore {
public void test(final String test) {
}
}
(I am aware that I can use a static method here as well, but it is not possible with the real life scenario)
I am using the following Lua code:
-- Always name this function "initCallbacks"
function initCallbacks(callbackStore)
callbackStore.test("test")
end
Which does not work as it is expecting userdata back, but I give it a string.
And I call the Lua code like this:
globals.load(new StringReader(codeTextArea.getText()), "interopTest").call();
CallbackStore callbackStore = new CallbackStore();
LuaValue initCallbacks = globals.get("initCallbacks");
initCallbacks.invoke(CoerceJavaToLua.coerce(callbackStore));
where the Lua code is returned by codeTextArea.getText()
Bottom line of my question is, how do I make my code running with test as an instance method?
When accessing member functions (in Lua objects in general, not just luaj) you have to provide the this argument manually as the first argument like so:
callbackStore.test(callbackStore,"test")
Or, you can use the shorthand notation for the same thing:
callbackStore:test("test")
I got a strange problem with a call to a Java method from JRuby.
In my Java class these methods are defined twice, and it appears JRuby calls the wrong one.
So I tried to use java_method, but I always got a:
TypeError: cannot convert instance of class org.jruby.RubyModule to class java.lang.Class
Here's my Java code:
public class Renderer {
...
public void addRenderer(IElementRenderer r) {
System.out.println("Added element render: " + r.getClass().toString());
basicRenderers.add(r);
rendererMap.put(r.elementClass(), r);
}
public void addRenderer(IBasicRenderer r) {
System.out.println("SHOULD NOT GO THERE !!");
basicRenderers.add(r);
}
}
and my JRuby code:
add_renderer = renderer.java_method :add_renderer, [Java::dragon.render.IElementRenderer]
add_renderer.call TextRenderer.new
I also tried with java_send but I got the same error:
renderer.java_send(:add_renderer, [Java::dragon.render.IElementRenderer], TextRenderer.new)
Next, I tried with:
renderer.add_renderer(TextRenderer.new.to_java(IElementRenderer))
This time no errors but the wrong method is called ...
How can I fix this problem?
You can fix that cannot convert instance of class org.jruby.RubyModule to class java.lang.Class using java.lang.Class.for_name
In your case, it is
add_renderer = renderer.java_method :add_renderer, [java.lang.Class.for_name("dragon.render.IElementRenderer")]
This is because java interfaces become Ruby Modules by default and the second argument to :java_method expects an array of Class objects.
You can print the matched method to see it is matching the intended method.
For example, I see below code is matching the println(String) on System.out.
>>java.lang.System.out.java_method "println", [java.lang.Class.for_name("java.lang.String")]
#<Method: Java::JavaIo::PrintStream#(java.lang.String)>
I've had problems like this before. It was many versions ago and I think JRuby's method matching algorithm has improvedd over time. Are you using the latest JRuby?
If nothing else works, you may need to add another method, or a wrapper class. Something that distinguishes your methods by name or number of parameters, not just parameter type.