I am using Java 8 Nashorn to execute a specific previously agreeed upon method. I could Invoke the specific method no problem. One thing that bugged me though is that when I load the script, it also executes it.
For example if file.js contains a print("hello world!") the scriptEngine.eval(new FileReader("./file.js") would execute and print hello world. I have to do this before I could invoke the specific method I want.
Is there a way to eval()/load the scripts without executing it?
Thanks
turns out You could do this by Casting engine to Compilable then call the compile function.
final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
final Compilable compilable = (Compilable) engine;
final Invocable invocable = (Invocable) engine;
final String statement = "function fetch(values) { return values['first_name'] + ' ' + values['last_name']; };";
final CompiledScript compiled = compilable.compile(statement);
This achieved what I want without needing to eval() it
Related
I have an external groovy file containing all common functions required to automate my web service testing. I reference those common functions by creating an instance of the Class defined within the external file. Now I have a situation to create an instance of the Class in first groovy test step and to use the same instance in other groovy test steps within my test case.
import groovy.lang.Binding
import groovy.util.GroovyScriptEngine
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
// location of script file is relative to SOAPUI project file.
String scriptPath = groovyUtils.projectPath + "\\Scripts\\"
// Create Groovy Script Engine to run the script.
GroovyScriptEngine gse = new GroovyScriptEngine(scriptPath)
// Load the Groovy Script file
externalScript = gse.loadScriptByName("CustomerQuotes.groovy")
def cq = externalScript.newInstance(context: context, log: log, testRunner: testRunner)
How do I achieve this? I need the reference of cq object in other groovy test steps to call the remaining common functions available within my external grooy file? Please help.
As per your question, the mentioned groovy script test step is placed in an arbitrary test case though it is not a natural fit.
The natural fit for the above script is to use Load Script which is at project level.
In the script, which is mentioned in the question, change below statements
From:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
// location of script file is relative to SOAPUI project file.
String scriptPath = groovyUtils.projectPath + "\\Scripts\\"
To:
def projectPath = new File(project.path).parent.toString()
String scriptPath = "${projectPath}/Scripts"
And keep the rest of the script same.
Place the script (after the above change) at Project level's Load Script.
Remove the groovy script test step from wherever it is because of the above mentioned reason.
At the end of the script, add the below statement. Basically using the groovy's Meta Programming feature to store the object for sharing.
project.metaClass.myObject = cq
Next step: How to re-use the object (which is the main question)?
Since, your object cq is added to project object, the same can be accessed in any of the groovy script test steps (at any suite or case) using below statement:
def cq = context.testCase.testSuite.project.myObject
//Now call the other methods using cq.method(arguments)
EDIT: It appears that the above solution works for simple/Primitive data types.
However, you have a class instance. For that some more changes are required.
Here is your complete Project level Load Script (includes your code snippet)
def projectPath = new File(project.path).parent.toString()
String scriptPath = "${projectPath}/Scripts"
GroovyScriptEngine gse = new GroovyScriptEngine(scriptPath)
def externalScript = gse.loadScriptByName("CustomerQuotes.groovy")
project.metaClass.myObject {
externalScript.newInstance(context: it, log: log, testRunner: it.testRunner)
}
And the script for Groovy Script test step in different test cases is as follows i.e., just calling the methods of your CustomerQuotes.groovy class.
def obj = context.testCase.testSuite.project.myObject(context)
obj.run()
Assuming that there is a method in the groovy file called run. Of course, you can use your own method.
EDIT 2:
There is another alternative approach too. You need to compile the groovy classes, create jar, copy it under SOAPUI_HOME/bin/ext directory. Of course, soapui tool needs to be restarted after that.
Now you can create instance and make the desired call to the methods as needed in any of the groovy script test steps.
I know how to call Groovy code from Java code, but can you, while running Groovy script script.groovy, ask a Java program to populate its varabies?
// script.groovy
import static org.mydomain.WiseJavaProgram.*;
pleasePopulateMeWithVariables();
println numberOfLegs // 2
// We didn't declare numberOfLegs in our sript, the Java program set it for us.
I guess maybe for that script.groovy should pass its environment to the pleasePopulateMeWithVariables method, but I haven't found how to do that. Is this even possible?
The case where I want to use such behavior: I have script.groovy, and also an undefined number of groovy scripts of which Java program knows, but of which script.groovy doesn't know, and I want to add what is declared in them to script.groovy. Basically those additional scripts are data, for example descriptions of various products and their features. This is easy if you run a Java program that creates GroovyShell and evaluates in it data-scripts and then script.groovy, but for me it is necessary that we initially call script.groovy, not the Java program.
Just as usual, while asking the question I was one step behind the answer -__-
The "environment" I was searching for is the Script object itself, available from script.groovy simply as this. So:
// script.groovy
import static org.mydomain.WiseJavaProgram.*;
populateWithVariables(this);
println numberOfLegs // 2
// WiseJavaProgram.java
public class WiseJavaProgram {
public static void populateWithVariables(Script script) {
script.evaluate('numberOfLegs = 2');
script.evaluate(new File("another.groovy"));
script.evaluate(new File("yetAnother.groovy"));
}
}
Im starting to explore jdk 8 new javascript engine nashorn and wanted to build some automating task scripts. I ve an issue, ive no idea how to evaluate a js file in scripting mode from javascript, using engine.eval() eg .
p.s: im not talking about jjs -scripting which is good but only works one way. I want the other way; make the engine evaluate in scripting mode from java
The easiest way is to add -Dnashorn.args=-scripting to you java command line.
After a lot of head scratching, i came up with a trick where i can actually launch my script's execution through a command line from a hand crafted System Process :
//tricking the nashorn engine with jjs command
public void evalScriptInScriptingMode(String fileName)
{
String[] args = new String[]{"jjs", "-scripting", fileName};
//This class is used to create operating system processes
ProcessBuilder pb = new ProcessBuilder(args);
pb.directory(null);
File log = new File("jjs_log.txt");
int i = 0;
while(log.exists())
{
i++;
log = new File("jjs" + i + "_log.txt");
}
pb.redirectErrorStream(true);
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
Process p = null;
try
{
p = pb.start(); //start the process which remains open
}
catch(IOException e)
{
e.printStackTrace();
}
}
You can pass arguments to the script engine via the NashornScriptEngineFactory.
import jdk.nashorn.api.scripting.NashornScriptEngineFactory
new NashornScriptEngineFactory()
.getScriptEngine( "-scripting" )
.eval( "" );
You can also use new NashornScriptEngineFactory().getScriptEngine("-scripting"); which will retrieve a new Nashorn ScriptEngine in scripting mode. This method is slightly better than using a System Process mainly because this automatically adds you classes to the nashorn classpath.
Basically, you can program classes in java and then use'em in javascript. If you do not need to be able to reference your classes in javascript then the System Process should do just fine and there won't be problems, ( if the machine on which this is running has jjs in their classpath )
I want to call a groovy script from the other groovy script... anybody can help me in this ASAP..
example :
Class A having some code and it should call from B
class A{
static main(args){
println "Hello.. calling A Class"
}
}
I want to create a new class like B.groovy
class B{
static main(args){
// I need code for this to call A.groovy
}
}
Putting the following at the top of your script will load the contents of a groovy file.
evaluate(new File("/path/to/script/MyScript.groovy"))
You could also add this to the groovy classpath if you need to do something like this often. Hope this helps.
additionally if you need to run other scripts from your script you could do the following...
def script = new GroovyShell();
def args = ['Hello World'];
script.run(new File("/path/to/script/MyScript.groovy"), args as String[]);
Too late for the party (any beer for me?) but here I´ll show you 2 more flavors:
1) Remember the concept of Java´s CLASSPATH? That aplies to Groovy (because Groovy is Java!):
"The CLASSPATH variable is one way to tell applications, including the JDK tools, where to look for user classes.”
In order to run the script B.groovy you have to inform the location of the A.groovy (A class):
groovy –cp c:\groovy\yourscripts c:\groovy\scripts\B.groovy
The command above is telling the runtime to look in the c:\groovy\yourscripts folder because there is where we have our classes and we need them to run the B.groovy script successfully.
2) Use the GroovyClassLoader to load your scripts at runtime and using the code!
Both ways solve your needs. Now the next question is when to use each?
I want to compile and execute a piece of Groovy that the user types in (in a DSL) at runtime. Is this possible in Groovy? And if so what's the best way to do it?
You can use the GroovyClassLoader:
def userScript = '''
(1..5).each {
println 'X' * it
}
'''
Class c = new GroovyClassLoader().parseClass( userScript )
c.newInstance().run()
Or you can use any of the other methods in the documentation for integrating Groovy.
you might want to use the concept of binding in groovy which helps you to inject variables from outside and process them.
http://mrhaki.blogspot.in/2010/08/groovy-goodness-store-closures-in.html