Get groovy script free variables in runtime - java

I want to know how get all free variables from Groovy script from Java code.
Groovy script:
Integer v = a - b;
Integer k = 6 * v;
return k > 0;
Call from java:
Binding binding = new Binding();
GroovyShell groovyShell = new GroovyShell(binding);
Script script = groovyShell.parse(...);
script.getFreeVariables(); // == set with "a","b". Want something like this.
I know rude way - script.run() and then catch exception. In exception I get name of var that I don't pass to the script.

groovy:
def s0=a+b+2
s1=a+b+1
a=b*2
a+b+3 //the last expression will be returned. the same as return a+b+3
java:
GroovyShell groovyShell = new GroovyShell();
Script script = groovyShell.parse(...);
Map bindings = script.getBinding().getVariables();
bindings.put("a",new Long(1));
bindings.put("b",new Long(2));
Object ret = script.run(); //a+b+3
//and if you changed variables in script you can get their values
Object aAfter = bindings.get("a"); //4 because `a=b*2` in groovy
Object bAfter = bindings.get("b"); //2 not changed
//also bindings will have all undeclared variables
Object s1 = bindings.get("s1"); //a+b+1
//however declared variables will not be visible - they are local
Object s0 = bindings.get("s0"); //null

by default groovy has dynamic resolver at runtime and not at compiletime.
you can try catch access to those properties:
1/
import org.codehaus.groovy.control.CompilerConfiguration;
abstract class MyScript extends groovy.lang.Script{
public Object getProperty(String name){
System.out.println("getProperty "+name);
return new Long(5);
}
}
CompilerConfiguration cc = new CompilerConfiguration()
cc.setScriptBaseClass( MyScript.class.getName() )
GroovyShell groovyShell = new GroovyShell(this.getClass().getClassLoader(),cc);
Script script = groovyShell.parse("1+2+a");
Object ret = script.run();
2/
GroovyShell groovyShell = new GroovyShell();
Script script = groovyShell.parse("1+2+a");
Map bindings = new HashMap(){
public Object get(Object key){
System.out.println ("get "+key);
return new Long(5);
}
}
script.setBinding(new Binding(bindings));
Object ret = script.run();

Related

Is it possible to get the process name of a Process object?

I have several process objects from which I'd like to get their names. Something like Thread-34 or Thread-74, etc.
I know I can build such with the pid, but is there something built in?
I know how to get their pid or info, but that doesn't help me to get their names.
ProcessBuilder pb = new ProcessBuilder(command);
p = pb.start();
p.pid()
p.info()
Is it possible (Java 15)?
Use p.info().command(). It returns the executable pathname of the process (reference). Note that it returns an Optional object: you need to check if it has a value before getting the pathname string out of it:
var pathname = p.info().command();
if (pathname.isPresent()) {
System.out.println(pathname.get());
}
If you would like to refer to your processes by custom names (like "Process-1", "Process-2", ...) then you can use a HashMap:
import java.util.HashMap;
public class App {
public static void main(String[] args) throws Exception {
// Create processes with names
var processes = new HashMap<String, Process>();
for (var i = 0; i < 5; ++i) {
var processBuilder = new ProcessBuilder("cmd");
var process = processBuilder.start();
var processName = "Process-" + (i + 1); // Name can be anything you want
processes.put(processName, process);
}
// List process names
for (var processName : processes.keySet()) {
System.out.println(processName);
}
// Refer to a process by name
System.out.println(processes.get("Process-3").pid());
}
}
Example output:
Process-1
Process-3
Process-2
Process-5
Process-4
12780

Correct way to instantiate a groovy class

I want to instatiate a groovy class and i have some concerns
My first choice is to use GroovyShell :
groovy-script:
class Foo {
public String doStuff(String stuff) {
return stuff + "_utils";
}
}
new Foo(); // ??
main-script :
GroovyShell shell = new GroovyShell();
Script script = shell.parse(new File(path));
def clazz = script.run();
String result = clazz.doStuff("test");
print(result); // test_utils;
The second option is to use GroovyClassLoader :
groovy-script
class Foo {
public String doStuff(String stuff) {
return stuff + "_utils";
}
}
main-script
GroovyClassLoader loader = new GroovyClassLoader();
Script script = loader.parseClass(new File(path))
Object clazz = script.newInstance();
Object[] args = new Object[1];
args[0] = "test";
String result = clazz.invokeMethod("doStuff", args);
print(result) // test_utils
Both will run fine, i would prefer to use GroovyShell because i use it everywhere in my current code, but i don't know if new Foo() inside my scripts can cause any memory leaks. Is it possible?
GroovyShell uses the default GroovyClassLoader. So if you don't need any extra specific features that are provided by GroovyClassLoader, you should stick to GroovyShell to keep it simple. GroovyShell and GroovyClassLoader are instances of garbage collected and I don't believe there are memory leaks for neither of them.
After #daggett's help i managed to find the solution that fits my needs.
I will not use groovy classes at all.
A simple example
utility groovy script :
String doStuff() {
return "doStuff";
}
String doStuff2(){
return "doStuff2";
}
calling the utility methods from the main groovy script
GroovyShell shell = new GroovyShell();
Script utilsScript = shell.parse(new File(PATH_TO_UTIL_SCRIPT));
String result = utilsScript.doStuff();
println(result); // doStuff;
String result2 = utilsScript.doStuff2();
println(result2); // doStuff2;
This is not the answer to the original question but since it fits my need i am fine.

Groovy serialization without class definition

I am trying to create a serializable interface implementation in groovy dynamically which could be send over the wire where it can be deserialized and executed with args. I have created anonymous interface implementation using map but it fails on serialization.
gcloader = new ​GroovyClassLoade​r()
script = "class X { public def x = [call: {y -> y+1}] as MyCallable }"​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
gclass = gcloader.parseClass(script)
x = gclass.newInstance().x​​
// serialzing x fails
I am not sure if a groovy closure is compiled to a random class name, which would make it impossible to deserialized even if it gets serialized. Is there a way to do this?
Here's a piece of code that might be helpful:
import groovy.lang.GroovyClassLoader
def gcLoader = new GroovyClassLoader()
def script = """
class X implements Serializable {
public def x = [
call: { y -> y + 1 }
]
}"""
def cls = gcLoader.parseClass(script)
def inst = cls.newInstance().x
def baos = new ByteArrayOutputStream()
def oos = new ObjectOutputStream(baos)
oos.writeObject(inst)
def serialized = baos.toByteArray()
def bais = new ByteArrayInputStream(serialized)
def ois = new CustomObjectInputStream(bais, gcLoader)
inst = ois.readObject()
assert 2 == inst.call(1)
public class CustomObjectInputStream extends ObjectInputStream {
private ClassLoader classLoader
public CustomObjectInputStream(InputStream ins, ClassLoader classLoader) throws IOException {
super(ins)
this.classLoader = classLoader
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
return Class.forName(desc.getName(), false, classLoader)
}
}
Basically, You need an instance of ObjectInputStream with custom ClassLoader.
According to my own limited research I came to conclusion that, In jvm there is no standard/popular library which could pickle code like in python which was the requirement i was primarily after. There are some ways to do it through URL classloaders etc but comes with some inherent complexities. I ended up just simply sending the code string and recompiling it whenever required on multiple machines.

SecureASTCustomizer: how to restrict loops?

I'm trying to restrict using loops(FOR and WHILE operators) in Groovy script.
I tried http://groovy-sandbox.kohsuke.org/ but it seems to be not possible to restrict loops with this lib.
Code:
final String script = "while(true){}";
final ImportCustomizer imports = new ImportCustomizer();
imports.addStaticStars("java.lang.Math");
imports.addStarImports("groovyx.net.http");
imports.addStaticStars("groovyx.net.http.ContentType", "groovyx.net.http.Method");
final SecureASTCustomizer secure = new SecureASTCustomizer();
secure.setClosuresAllowed(true);
List<Integer> tokensBlacklist = new ArrayList<>();
tokensBlacklist.add(Types.KEYWORD_WHILE);
secure.setTokensBlacklist(tokensBlacklist);
final CompilerConfiguration config = new CompilerConfiguration();
config.addCompilationCustomizers(imports, secure);
Binding intBinding = new Binding();
GroovyShell shell = new GroovyShell(intBinding, config);
final Object eval = shell.evaluate(script);
Whats wrong with my code or probably some one knows how I can restrict some loops or operators?
WHILE and FOR are statements. You should rather try adding them as statementsBlacklist instead of tokenBlacklist.
List<Class> statementBlacklist = new ArrayList<>();
statementBlacklist.add( org.codehaus.groovy.ast.stmt.WhileStatement );
secure.setStatementsBlacklist( statementBlacklist );

How to convert a string into a piece of code (Factory Method Pattern?)

Let's say we have a String like this:
String string2code = "variable = 'hello';";
How could we convert that String to a piece of code like this?:
variable = "hello";
GroovyShell is the answer:
String string2code = "variable = 'hello'; return variable.toUpperCase()";
def result = new GroovyShell().evaluate string2code
assert result == "HELLO"
If you're into more complex stuff later, you can compile whole classes using GroovyClassLoader.
private static Class loadGroovyClass( File file ) throws MigrationException {
try {
GroovyClassLoader gcl = new GroovyClassLoader( ExternalMigratorsLoader.class.getClassLoader() );
GroovyCodeSource src = new GroovyCodeSource( file );
Class clazz = gcl.parseClass( src );
return clazz;
}
catch( CompilationFailedException | IOException ex ){
...
}
}
Maybe you can take a look a Janino
Janino is a small java compiler than not only can compile source files, it can compile expressions like the one you have.

Categories

Resources