I'm essentially trying to create a CLI with Groovy. I have a whole JavaFX GUI set up in Java and I want to be able to type in groovy script to run different functions inside a groovy script.
For example, say I have this script:
void meow() {
println "walrus"
}
I want to be able to type in "meow();" and press enter and evaluate it using the script as a reference.
I've tried using
shell.evaluate(inputStr, "src/Server/Scripting/CommandLineScript.groovy");
but to no avail; it just comes up with the error:
groovy.lang.MissingMethodException: No signature of method: CommandLineScript.meow() is applicable for argument types: () values: []
I can call other standard functions, such as:
shell.evaluate("println 'Hello World!';");
but I just can't run my own methods... How to solve it?
The following worked for me.
evaluate(new File("/Users/jellin/meow.groovy"))
I did change the meow.groovy file to execute the method within the file.
void meow() {
println "walrus"
}
meow()
One issue is I don't see a way to pass a parameter to the calling script.
I have used the following before, you can pass parameters as part of the binding.
String script = "full path to the script"
GroovyScriptEngine gse = new GroovyScriptEngine()
Binding binding = new Binding();
Object result = gse.run(script, binding)
Also, you might be able to simply reference the other scripts as classes and execute the run method on them.
There is also an AST transformation that can be used to have scripts extend a base script.
See here for more info
http://mrhaki.blogspot.com/2014/05/groovy-goodness-basescript-with.html
Thanks for your time guys; after a little more searching (always after posting a question do I find the answer in research >,<), I found that you can set a base class for the GroovyShell... I did it this way:
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
loader.addClasspath("src/ScriptLoc/");
binding = new Binding();
CompilerConfiguration compConfig = new CompilerConfiguration();
compConfig.setScriptBaseClass("ScriptName");
shell = new GroovyShell(loader, binding, compConfig);
I thought there would be a way to do it, and there it is... Now whenever I need to evaluate a script from the text box, I can just evaluate it and it evaluates it in the context of the base script.
Related
I just started learning about groovy and trying to transpose my java code to groovy scripts. Usually java allows you have a class with only methods that you can call from other classes. I wanted to translate that to groovy. I have in one file - lets call it File1- a method like this:
def retrieveData(String name){
// do something
}
and in the second file, File2, I call File1 like this:
def file1Class = this.class.classLoader.parseClass(new File("../File1.groovy"))
and then try to call the method in File1 like this:
def data = file1Class.retrieveData("String")
but it keeps giving me this error - MissingMethodException:
groovy.lang.MissingMethodException: No signature of method: static File1.retrieveData() is applicable for argument types: (java.lang.String) values: [String] Possible solutions: retrieveData(java.lang.String)
so it does recognize that I am sending in the correct number of parameters and even the correct object, but it isn't running the method as it should?
Is there something I am missing? I tried to remove the object definition from the method - in other words - like this:
def retrieveData(name){
// do something
}
but that didn't work either. I am clueless about what the next step would be. Can anyone please help push me in the right direction? I would greatly appreciate it.
See the answer provided in this StackOverflow reponse.
Use the GroovyScriptEngine class. What does the GroovyScriptEngine do? From the docs:
Specific script engine able to reload modified scripts as well as
dealing properly with dependent scripts.
See the example below.
def script = new GroovyScriptEngine( '.' ).with {
loadScriptByName( '..\File1.groovy' )
}
this.metaClass.mixin script
retrieveData()
Note how we use the loadScriptByNamemethod to
Get the class of the scriptName in question, so that you can
instantiate Groovy objects with caching and reloading.
This will allow you to access Groovy objects from files however you please.
I've been struggling with this problem for two days now and no resource I've found have been able to solve it.
I am trying to call a java class (added the link at the bottom) from Matlab (version 7.13.0.564 (R2011b)). I've compiled the java class using java 1.6 into a .class file and also added the path to the folder where the file is situated using javaaddpath (I've of course checked that the path is correct in the list of dynamic paths). However, when I try to call the class from Matlab using javaMethod('main','PerlinNoiseGenerator','') I get the error:
"No class PerlinNoiseGenerator can be located on Java class path"
I would be extremely grateful if someone with experience in calling java from Matlab could put together a short tut on how to do this. I am probably going to distribute my code so I kinda need to set the java path dynamically and from what I've read it really should be possible although I've seen post that indicate that it could be the cause of the problem.
http://svn.j3d.org/code/tags/Xj3D-M10/src/java/org/j3d/texture/procedural/PerlinNoiseGenerator.java
Usually I create jar files that contain java classes. I also had problems loading individual java classes before. In your case I did the following on xubuntu 13.04 x64 and Matlab 2013a x64 to load your particular class:
Compile it using java 6 (not the default 7) with option -d . to create a set of package folders, as your class defines a package org/j3d/texture/proecedural/ etc:
/usr/lib/jvm/java-6-openjdk-amd64/bin/javac -d . PerlinNoiseGenerator.java
This will compile the class and make in the current director (thus .) the set of package folders.
Make jar file containing your class again using jar from java 6. I named it javaNoise.jar:
/usr/lib/jvm/java-6-openjdk-amd64/bin/jar cf javaNoise.jar ./org/j3d/texture/procedural/PerlinNoiseGenerator.class
In Matlab, in the directory where javaNoise.jar is:
javaaddpath('./javaNoise.jar');
Create object of your java class:
png=org.j3d.texture.procedural.PerlinNoiseGenerator()
% results in: png = org.j3d.texture.procedural.PerlinNoiseGenerator#3982a033
To test it, I just generated some 1D noise:
png.noise1(1.2)
ans = -0.0960
Hope this helps.
P.S.
javaMethod('main','PerlinNoiseGenerator','') wont work because this class has no main method:-).
Your notation to the compiler of the constructor is a polymorphic class meaning "use appropriate constructor that is called at runtime".
public PerlinNoiseGenerator()
public PerlinNoiseGenerator(int seed)
The first form with no argument can be called but is irrelevent because the line with this(DEFAULT_SEED) attempts to call itself but only one constructor is allowed used
Second constructor has int for an argument but requires being loaded by an already loaded class.
Use the first version and change the case sensitive name of the one with the argument and remove this(DEFAULT_SEED) from it replace with the method name(the one you changed from a constructor that has the argument).
e.g. public perlinNoiseGenerator(int seed)
note: by convention java code method names start with a lower-case letter.
A final note, java arguments from the command line come in as "String" data type through the "main" method, a starter method for applications (gui or command prompt).
The first argument on the main method argument is the first commandline argument.
public static void main(String[] Args){
new PerlinNoiseGenerator(Args); // recursive class call
}//end main method
int[] args; // global
public PerlinNoiseGenerator(String[] Args){
int arglength=Args.length();
args = new int[arglength];
for(int cnt=0;cnt<arglength;cnt++){
Args[cnt].trim();
args[cnt]=new Integer(Args[cnt]).intValue();
}//enfor
perlinNoiseGenerator(args[0]); // call method
}//end constructor
I have a Groovy file that looks like this (currently).
main.groovy
import org.packages.mystuff.JavaClassIAmUsing;
public class MyObject {
def rate(item){
def o = evaluate(new File (new File(getClass().protectionDomain.codeSource.location.path).parent),"CommonFunctions.groovy");
println o.whoami();
}
}
i have another groovy file called
CommonFunctions.groovy
def whoami() {return 'no body';}
I'm trying to include the CommonFunctions script in to main script, BUT the location of the script are not known at build time (i.e. i can not hardcode a absolute file path in the script or absoulte path of the java process in relation to where the scripts will be stored).
All i know is that the scripts will be together or at a location relative to the calling script (say sub directory).
I've attempted to try and location the calling script location, but i get the error
No signature of method: MyObject.evaluate()
How can i referance this script, considering the main script is accessed at runtime using a GroovyClassLoader.parseClass(File) method.
I'm not really sure why you want to do it this way, I think it would be much simpler to make a class of CommonsFunctions that you could instantiate normally and use everywhere.
However, it is possible to achieve what you want; with Groovy, there are not that many limitations...
There are two problems with your suggested solution:
getClass() inside your MyObject class naturally refers to ... the MyObject class, so your attempt to find the location of the script will fail. You're on the right track, but you need to resolve the script location using the surrounding Script class.
evaluate doesn't really work the way you think it does. The result of the evaluate method is the result of the script, not an instance of the Script class. One way to remedy this, is to rewrite the methods in CommonFunction as closure properties. These properties will be available in the shell Binding object when evaluating the script.
So, with these rewrites, you end up with something like this:
main.groovy
class MyObject {
def scriptDir
def rate(item) {
def commonFunctionsScriptFile = new File(scriptDir, "CommonFunctions.groovy")
def binding = new Binding()
new GroovyShell(binding).evaluate(commonFunctionsScriptFile)
println binding.variables.whoami()
}
}
scriptFile = new File(getClass().protectionDomain.codeSource.location.path)
new MyObject(scriptDir: scriptFile.parentFile).rate(null)
Here, the script file location is resolved in the script, not in the inner class.
CommonFunctions.groovy
whoami = { 'no body' }
Here, whoami is no longer a method, but a closure property which will be added to the binding. Make sure that you don't prefix this property with def, since then it will be a local variable instead of a property added to the binding object.
Output after these rewrites is the expected: no body.
Is there a way to obtain reflection data on functions declared in a Groovy script that has been evaluated via a GroovyShell object? Specifically, I want to enumerate the functions in a script and access annotations attached to them.
Put this to the last line of Groovy script - it will serve as a return value from the script, a-la:
// x.groovy
def foo(){}
def bar(){}
this
Then, from Java code you can do the following:
GroovyShell shell = new GroovyShell();
Script script = (Script) shell.evaluate(new File("x.groovy"));
Now it seems that there's no option to introspect the annotations of Groovy script from Java directly. However, you can implement a method within the same Groovy script and call that one from Java code, for instance:
//groovy
def test(String m){
method = x.getMethod(m, [] as Class[])
assert method.isAnnotationPresent(X)
}
//java
script.getMetaClass().invokeMethod(script, "test", "foo");
After some experimenting, I found this to be the easiest way:
GroovyShell shell = new GroovyShell();
Script script = (Script)shell.parse(new FileReader("x.groovy"));
Method[] methods = script.getClass().getMethods();
The method array has all of the functions defined in the script and I can get the annotations from them.
I am trying to call a user defined Matlab Function(M file) which takes 3 arguments(Java Strings) from my Java application which is developed in Eclipse. At the moment I am able to call proxy.eval and proxy.feval methods with the functions/commands like disp or sqr. But when i try to invoke a user-defined function it says on the matlab console that there is no such function defined like that and on the Java console MatlabInvocationException occurs.
Then I tried with a simple user-defined function which takes no arguments and just has single line disp('Hello') but still the result is same. So I think rather than a type conversion problem there is something wrong with how user-defined functions are getting invoked.
Please can anyone help me soon? I am meeting the deadline very soon for this project. I would be so thankful if someone can come up with a solution. (Mr Joshuwa Kaplan, is there any guide on solving an issue like this in your posts? I tried but found nothing)
Thanks in advance
You must have any user-defined m-files on the MATLAB search path, just as if you were working normally inside MATLAB.
I tested with the following example:
C:\some\path\myfunc.m
function myfunc()
disp('hello from MYFUNC')
end
HelloWorld.java
import matlabcontrol.*;
public class HelloWorld
{
public static void main(String[] args)
throws MatlabConnectionException, MatlabInvocationException
{
// create proxy
MatlabProxyFactoryOptions options =
new MatlabProxyFactoryOptions.Builder()
.setUsePreviouslyControlledSession(true)
.build();
MatlabProxyFactory factory = new MatlabProxyFactory(options);
MatlabProxy proxy = factory.getProxy();
// call builtin function
proxy.eval("disp('hello world')");
// call user-defined function (must be on the path)
proxy.eval("addpath('C:\\some\\path')");
proxy.feval("myfunc");
proxy.eval("rmpath('C:\\some\\path')");
// close connection
proxy.disconnect();
}
}
We compile and run the Java program:
javac -cp matlabcontrol-4.0.0.jar HelloWorld.java
java -cp ".;matlabcontrol-4.0.0.jar" HelloWorld
a MATLAB session will open up, and display the output:
hello world
hello from MYFUNC
You could also add your folder to the path once, then persist it using SAVEPATH. That way you won't have to do it each time.