I have 2 scripts, where I am trying to test passing an argument, and it fails. I examined the documentation for GroovyScriptEngine but it doesn't seem to handle the case where I want to pass a arg rather than a property value pair (in a binding).
Here is the error I get:
C:\AeroFS\Work\Groovy_Scripts>groovy scriptengineexample.groovy
hello, world
Caught: groovy.lang.MissingPropertyException: No such property: args
for class: hello
groovy.lang.MissingPropertyException: No such property: args for
class: hello
at hello.run(hello.groovy:4)
at Test.main(scriptengineexample.groovy:14)
Here are my scripts:
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
import groovy.util.ResourceException ;
import groovy.util.ScriptException ;
import java.io.IOException ;
public class Test {
public static void main( String[] args ) throws IOException,
ResourceException, ScriptException {
GroovyScriptEngine gse = new GroovyScriptEngine( [ '.' ] as String[] )
Binding binding = new Binding();
binding.setVariable("input", "world");
gse.run("hello.groovy", binding);
System.out.println( "Output: " + binding.getVariable("output") );
}
}
And this one:
//hello.groovy
println "hello.groovy"
for (arg in this.args ) {
println "Argument:" + arg;
}
Hello is looking for a string array in the binding called args. This is automatically provided to you when you run the script via the command line, but if you are running it outside of that context, you must add it to the Binding yourself:
This will pass the arguments sent to Test through to Hello as-is:
public class Test {
public static void main(String[] args) {
Binding b = new Binding()
b.setVariable("args", args)
Hello h = new Hello(b);
h.run()
}
}
If you want to send specific arguments, you must construct the array yourself:
public class Test {
public static void main(String[] args) {
Binding b = new Binding()
b.setVariable("args", ["arg1", "arg2", "etc."])
Hello h = new Hello(b)
h.run()
}
}
Even simpler, the Binding class has a Constructor which takes a String[], and add it as 'args' so you can just do this:
public class Test {
public static void main(String[] args) {
new Hello(new Binding(args)).run();
}
}
Related
I am using groovy 2.4.12 with Oracle JVM 1.8. I am trying to understand a bit how groovyc converts the scripts written by end users.
To that end I wrote this simple script:
println 'Hello World`
This was compiled to bytecode using groovyc hello.groovy. Finally, I decompiled the hello.class to get the following code:
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class hello extends Script {
public hello() {
CallSite[] var1 = $getCallSiteArray();
}
public hello(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, hello.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, "Hello World");
}
}
This looks like a typical Java class except I cannot figure out where $getCallSiteArray() method is defined. It is definitely not in this class and neither is it a public or protected member of groovy.lang.Script. So my question is where is this method implemented?
use another decompiler to see it
//
// Decompiled by Procyon v0.5.36
//
public class A extends Script
{
private static /* synthetic */ SoftReference $callSiteArray;
public A() {
$getCallSiteArray();
}
public A(final Binding context) {
$getCallSiteArray();
super(context);
}
public static void main(final String... args) {
$getCallSiteArray()[0].call((Object)InvokerHelper.class, (Object)A.class, (Object)args);
}
public Object run() {
return $getCallSiteArray()[1].callCurrent((GroovyObject)this, (Object)"hello world");
}
private static /* synthetic */ CallSiteArray $createCallSiteArray() {
final String[] array = new String[2];
$createCallSiteArray_1(array);
return new CallSiteArray((Class)A.class, array);
}
private static /* synthetic */ CallSite[] $getCallSiteArray() {
CallSiteArray $createCallSiteArray;
if (A.$callSiteArray == null || ($createCallSiteArray = A.$callSiteArray.get()) == null) {
$createCallSiteArray = $createCallSiteArray();
A.$callSiteArray = new SoftReference($createCallSiteArray);
}
return $createCallSiteArray.array;
}
}
I am trying to use Groovy in order to convert string to the reflection code but I have "No such property" exception.
I have tried to make global all variables, change the reflection code and put #Field notation but problem still remaining. I put Groovy code inside "runTestSamples()".
MainClass - Test2
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.jacoco.agent.AgentJar;
import org.jacoco.core.analysis.Analyzer;
import org.jacoco.core.analysis.CoverageBuilder;
import org.jacoco.core.analysis.IClassCoverage;
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.data.SessionInfoStore;
import org.jacoco.core.instr.Instrumenter;
import org.jacoco.core.runtime.IRuntime;
import org.jacoco.core.runtime.LoggerRuntime;
import org.jacoco.core.runtime.RuntimeData;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
public class Test2 {
private Runnable targetInstance;
public Class<?> targetClass;
private static HashMap<Integer, String> testSamples;
private static HashMap<String, Integer> coverageData;
public String targetName;
public IRuntime runtime;
public Instrumenter instr;
public InputStream original;
public byte[] instrumented;
public RuntimeData data;
public MemoryClassLoader memoryClassLoader;
static Test2 t2 = new Test2();
int a;
public static void main(String[] args) throws Exception {
testSamples = new HashMap<Integer, String>();
coverageData = new HashMap<String, Integer>();
try {
t2.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
public void execute() throws Exception {
testSamples = new HashMap<Integer, String>();
coverageData = new HashMap<String, Integer>();
targetName = SUTClass.class.getName();
runtime = new LoggerRuntime();
instr = new Instrumenter(runtime);
original = getTargetClass(targetName);
instrumented = instr.instrument(original, targetName);
original.close();
data = new RuntimeData();
runtime.startup(data);
memoryClassLoader = new MemoryClassLoader();
memoryClassLoader.addDefinition(targetName, instrumented);
targetClass = (Class<? extends Runnable>) memoryClassLoader.loadClass(targetName);
targetInstance = (Runnable) targetClass.newInstance();
// Test samples
runTestSamples();
targetInstance.run();
final ExecutionDataStore executionData = new ExecutionDataStore();
final SessionInfoStore sessionInfos = new SessionInfoStore();
data.collect(executionData, sessionInfos, false);
runtime.shutdown();
final CoverageBuilder coverageBuilder = new CoverageBuilder();
final Analyzer analyzer = new Analyzer(executionData, coverageBuilder);
original = getTargetClass(targetName);
analyzer.analyzeClass(original, targetName);
original.close();
for (final IClassCoverage cc : coverageBuilder.getClasses()) {
coverageData.put("coveredInstructions", cc.getInstructionCounter().getCoveredCount());
}
System.out.println(coverageData.get("coveredInstructions"));
System.out.println(a);
}
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<String, byte[]>();
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
#Override
protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.loadClass(name, resolve);
}
}
private InputStream getTargetClass(final String name) {
final String resource = '/' + name.replace('.', '/') + ".class";
return getClass().getResourceAsStream(resource);
}
public void runTestSamples() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException,
NoSuchMethodException, SecurityException, ClassNotFoundException {
// Test case
targetClass.getMethod("f", int.class, int.class).invoke(targetInstance, 2, 9);
// Groovy String to code
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding);
Object value = shell.evaluate("targetClass.getMethod(\"f\", int.class, int.class).invoke(targetInstance, 2, 9);");
}
}
Exception
groovy.lang.MissingPropertyException: No such property: targetClass for class: Script1
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:65)
at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:309)
at Script1.run(Script1.groovy:1)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:437)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:475)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:446)
at Test2.runTestSamples(Test2.java:119)
at Test2.execute(Test2.java:66)
at Test2.main(Test2.java:43)
the problem in this code:
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding);
Object value = shell.evaluate("targetClass.getMethod(\"f\", int.class, int.class).invoke(targetInstance, 2, 9);");
when you call shell.evaluate imagine that you call absolutely new class that doesnot know anything about your current variables like targetClass
so, GroovyShell telling that there is no such property: targetClass
to fix it - you have just to populate binding - pass the variables values and names that should be visible inside the shell.evaluate(...).
Binding binding = new Binding();
binding.setVariable("target", targetClass) //pass targetClass as target variable name
binding.setVariable("instance", targetInstance)
GroovyShell shell = new GroovyShell(binding);
Object value = shell.evaluate("target.getMethod(\"f\", int.class, int.class).invoke(instance, 2, 9)");
another point - groovy is already dynamic language and you could simplify your nested script from this:
target.getMethod("f", int.class, int.class).invoke(instance, 2, 9)
to this:
instance."f"(2, 9)
and finally maybe you don't need to use the groovyshell because the following code dynamycally calls the method:
class A{
def f(int a, int b){ a+b }
}
def instance = new A()
def method = "f"
def params = [2,9]
println instance."${method}"(params)
I'm testing Groovy but I can't figure out how to properly call GroovyScriptEngine. It keeps producing an error below.
org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack
Song.Groovy
class Song {
def args;
{
println "Song has been called." + args;
}
String getArtist(){
return "sdfsdf";
}
public String toString(){
return "Hey!";
}
}
Java Main ->
String[] paths = { "C:\\Users\\User\\workspace\\GroovyTest\\src\\groovy" };
GroovyScriptEngine gse = new GroovyScriptEngine(paths);
Binding binding = new Binding();
Object s = "Default...";
binding.setVariable("args", s);
gse.run("Song.groovy", binding);
the args variable also produce null..
What to do ?
You are loading a class!
If you want to test your class, try something like this in the end of your Song.groovy:
// Instantiate an object of your class and use some methods!
def song = new Song()
println song.getArtist();
When you run
gse.run("Song.groovy", binding);
You are basically loading your class, but you are not doing anything with it.
See this example here
(Posted on behalf of the OP):
Working code:
Test1.java
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
public class Test1 {
public static void main(String[] args) throws Exception {
String[] paths = { "C:\\Users\\User\\workspace\\GroovyTest\\src\\groovy" };
GroovyScriptEngine gse = new GroovyScriptEngine(paths);
Binding binding = new Binding();
binding.setVariable("args", "Test Data");
String result = (String) gse.run("File1.groovy", binding);
System.out.println("Groovy Result: " + result);
}
}
File1.groovy
package groovy;
class Greeter {
String sayHello(String data) {
def greet = data;
return greet
}
}
static void main(String[] args) {
def greeter = new Greeter()
return greeter.sayHello(args);
}
My app has the following directory structure:
myapp/
src/
com.me.myapp/
Driver.groovy
Fizz.groovy
stages (package)
AbstractStage.groovy
impl (package)
DefaultStage.groovy
Where:
class Driver {
static void main(String[] args) {
AbstractStage stage1 = new DefaultStage()
stage1.derp
}
}
abstract class AbstractStage {
Set<Fizz> fizzes = new HashSet<Fizz>()
void derp() {
println "I have " + fizzes.size + " attached fizzes!"
}
abstract void doSomething()
}
class DefaultStage extends AbstractStage {
Set<Fizz> executedFizzes = new HashSet<Fizz>()
#Override
void doSomething() {
// TODO: Implement
}
}
When I run this I get:
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: derp for class:com.me.myapp.stages.impl.DefaultStage
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:50)
at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:231)
at com.me.myapp.Driver.main(Driver.groovy:6)
...where Driver.groovy:6 corresponds to the line that calls derp.
What's going on here?
derp is a method, not a property, so you need to invoke it:
static void main(String[] args) {
AbstractStage stage1 = new DefaultStage()
stage1.derp() // You need the '()' here.
}
I have a class that implements GroovyInterceptable, and I thought it should make all method invocation to go through the 'invokeMethod'. But I find that this is not the case.
class Test implements GroovyInterceptable{
String name
#Override
Object invokeMethod(String name, Object args) {
def metaMethod = this.metaClass.getMetaMethod(name, args)
return metaMethod.invoke(this, "BAR")
}
public static void main(String[] args) {
Test.metaClass.NEWATTRIBUTE = null
def test = new Test()
test.NEWATTRIBUTE = "MEOW1"
println test.NEWATTRIBUTE // shouldnt this be BAR ?
test.setNEWATTRIBUTE("MEOW2")
println test.NEWATTRIBUTE // prints 'BAR'
// much simpler example ....
test.name = "MOOO"
println test.name // shouldnt this be BAR ?
test.setName("MOOO")
println test.name // prints 'BAR'
}
}
I think I read somewhere, that
test.NEWATTRIBUTE = "MEOW1"
doesnt really access the field directly. Groovy will still call the setter method - and hence the invokeMethod should get invoked, shouldnt it?
Thanks
Alex
When setting a property, Groovy invokes setProperty. Add this method to the class:
void setProperty(String prop, val) {
System.out.println "set property $prop with $val"
}
WillP: Thanks a lot. Here is the complete code:
class Test implements GroovyInterceptable{
String name
#Override
Object invokeMethod(String name, Object args) {
def metaMethod = this.metaClass.getMetaMethod(name, args)
return metaMethod.invoke(this, "BAR")
}
void setProperty(String prop, val) {
getMetaClass().setProperty(this, prop, "BAR2");
}
public static void main(String[] args) {
Test.metaClass.NEWATTRIBUTE = null
def test = new Test()
test.NEWATTRIBUTE = "MEOW1"
println test.NEWATTRIBUTE // prints BAR2
test.setNEWATTRIBUTE("MEOW2")
println test.NEWATTRIBUTE // prints BAR
test.name = "MOOO"
println test.name // prints BAR2
test.setName("MOOO")
println test.name // prints BAR
}
}
as it turns out, setProperty() is called regardless the class implements GroovyInterceptable or not. But invokeMethod() is only called when the class implements GroovyInterceptable