I am building a groovy-based tool and as an add-in i'd like to provide an interactive command-line, I have this partially working but the binding doesn't keep state between GroovyShell.evaluate() calls, I've gone through the groovy documentation and they have an example using a class called InteractiveGroovyShell, which is not available on version 2.0.x.
Is there a way to configure normal groovy shell to achieve this functionality?
Here is a simplified version of how I'm creating the groovy shell right now:
CompilerConfiguration config = new CompilerConfiguration();
Binding binding = new Binding();
shell = new GroovyShell(binding, config);
shell.evaluate("def a = 20");
shell.evaluate("println a"); //this throws an exception telling the variable does not exist
shell.evaluate("def a = 20");
Instead of def a = 20 you need just a = 20. Each evaluate call parses and compiles a separate script, and declarations (whether with def or with an explicit type such as int a = 20) become local variables in that specific script and do not store anything in the binding. Without the def you have a plain assignment to an otherwise undeclared variable, which will go into the binding and so be visible to later evaluate calls.
You should reuse the same binding for different shells. The binding itself will maintain the state:
import org.codehaus.groovy.control.CompilerConfiguration
def binding = new Binding()
def shell = new GroovyShell(binding)
shell.evaluate("a = 5")
assert binding.variables == [a:5]
shell.evaluate("println a; b = 6")
assert binding.variables == [a:5, b:6]
def shell2 = new GroovyShell(binding)
// even in a new shell the binding keep the state
shell2.evaluate("c = 7")
assert binding.variables == [a:5, b:6, c:7]
Worked in groovy 2.0.5
Related
I am trying to run Guava reflections in my AWS Lambda function but it seems to not work in production..
The Code i am trying to run is supposed to create a Map<String, Class> with class name and class.
Code:
val converterClassMap by lazy {
val cl = ClassLoader.getSystemClassLoader()
ClassPath.from(cl).getTopLevelClasses("converters").asSequence().mapNotNull { it.load().kotlin }
.filter { it.simpleName?.endsWith("Converter") == true }
.associateBy( { it.simpleName }, { it } )
}
Running this code locally works perfectly, but running it in production on a lambda return an error where the map is empty.
Key PaginationConverter is missing in the map.: java.util.NoSuchElementException
Has anyone else run into this problem?
One more case. You have the
val cl = ClassLoader.getSystemClassLoader()
the line in the code. It means it takes the system classloader to scan for classes.
Try using
class SomeClassFromYouCodeNotALibrary
val cl = SomeClassFromYouCodeNotALibrary::class.java.classLoader
That one will work stable, independent from the number of classloaders, that are used in the application. AWS Lambda runtime may have specific classloaders, for example.
If it does not work, try logging the classloader type and classpath, e.g. println(cl) and println((cl as? URLClassLoader).getURLs().joinToString(", "))
I'm trying to update a maven plugin that's written in groovy to use an external JVM if it's available, otherwise, just use the default. My code changes look something like this:
def jvmExecutable = null;
if (someCondtion = true) {
jvmExecutable = "something"
}
def ant = new AntBuilder()
ant.java(fork: "${fork}", jvm: "${jvmExecutable}"....)
Is there a way in Groovy to leave off the jvm: "${jvmExecutable}" directive if jvmExecutable is null? The Groovy Ant task expects an executable there if jvm is specified, but I'd like it to use it's default if I don't specify something.
Essentially, if jvmExecutable != null do this
ant.java(fork: "${fork}", jvm: "${jvmExecutable}", ....)
or if jvmExecutable == null do this
ant.java(fork: "${fork}", ....)
Thank you!
when you pass named parameters into method you are actually building hashmap
so this code
ant.echo(message:"hello", level:"error")
equals to this one
ant.echo( [message:"hello", level:"error"] )
finally you want to keep in the map only valid values. like this:
ant.echo( [message:"hello", level:null].findAll{it.value!=null} )
I am trying to start a Kafka server form Java
Specifically, how can I translate this line of Scala into a line of Java?
private val server = new KafkaServer(serverConfig, kafkaMetricsReporters = reporters)
I can create the serverConfig easily, but I can't seem to be able to create the kafkaMetricsReporters parameter.
Note: I can create a KafkaServerStartable but I would like to create a normal KafkaServer to avoid the JVM exiting in case of error.
Apache Kafka version 0.11.0.1
The kafkaMetricsReporters parameter is a scala Seq.
You can either:
Create a Java collection and convert it into a Seq:
You need to import scala.collection.JavaConverters:
List<KafkaMetricsReporter> reportersList = new ArrayList<>();
...
Seq<KafkaMetricsReporter> reportersSeq = JavaConverters.asScalaBufferConverter(reportersList).asScala();
Use KafkaMetricsReporter.startReporters() method to create them for you from your configuration:
As KafkaMetricsReporter is a singleton, you need to use the MODULE notation to use it:
Seq<KafkaMetricsReporter> reporters = KafkaMetricsReporter$.MODULE$.startReporters(new VerifiableProperties(props));
Also the KafkaServer constructor has 2 other arguments that are required when calling it from Java:
time can easily be created using new org.apache.kafka.common.utils.SystemTime()
threadNamePrefix is an Option. If you import scala.Option, you'll be able to call Option.apply("prefix")
Putting it all together:
Properties props = new Properties();
props.put(...);
KafkaConfig config = KafkaConfig.fromProps(props);
Seq<KafkaMetricsReporter> reporters = KafkaMetricsReporter$.MODULE$.startReporters(new VerifiableProperties(props));
KafkaServer server = new KafkaServer(config, new SystemTime(), Option.apply("prefix"), reporters);
server.startup();
I want to embed Groovy to enable scripting capabilities in my Java application. I want to use static type checking, and in addition I want to pass some additional (global) variables to the script. Here is my configuration:
String script = "println(name)"; // this script was entered by the user
// compiler configuration for static type checking
CompilerConfiguration config = new CompilerConfiguration();
config.addCompilationCustomizers(new ASTTransformationCustomizer(CompileStatic.class));
// compile the script
GroovyShell shell = new GroovyShell(config);
Script script = shell.parse(script);
// later, when we actually need to execute it...
Binding binding = new Binding();
binding.setVariable("name", "John");
script.setBinding(binding);
script.run();
As you can see, the user-provided script uses the global variable name, which is injected via script.setBinding(...). Now there is a problem:
If I declare the variable name in the user script (e.g. String name;), then the binding has no effect because the variable already exists in the script.
If I do not declare the variable in the script, the static type checker will (rightfully) complain that name was not declared.
The question is: how do I solve this? How can I tell the type checker that the script will receive a global variable of a certain type when it is called?
From the doc,
You can use extensions parameter,
config.addCompilationCustomizers(
new ASTTransformationCustomizer(
TypeChecked,
extensions:['robotextension.groovy'])
)
Then add robotextension.groovy to your classpath:
unresolvedVariable { var ->
if ('name'==var.name) {
storeType(var, classNodeFor(String))
handled = true
}
}
Here, we’re telling the compiler that if an unresolved variable is found and that the name of the variable is name, then we can make sure that the type of this variable is String.
I installed Groovy.
And I am trying to run groovy scripts from a command prompt that I created using Java, like so:
Runtime.getRuntime().exec("groovy");
So if I type in "groovy" to the command line, this is what I get:
>>>groovy
Cannot run program "groovy": CreateProcess error=2, The system cannot find the file specified
Does anyone have an idea as to what might be going wrong? Should I just use Groovy's implementation of exec? Like:
def processBuilder=new ProcessBuilder("ls")
processBuilder.redirectErrorStream(true)
processBuilder.directory(new File("Your Working dir")) // <--
def process = processBuilder.start()
My guess is that it wouldn't matter whether using Java's implementation or Groovy's implementation.
So how do I run a groovy script?
The way originally described in the question above calling the groovy executable invokes a second Java runtime instance and class loader while the efficient way is to embed the Groovy script directly into the Java runtime as a Java class and invoke it.
Here are three ways to execute a Groovy script from Java:
1) Simplest way is using GroovyShell:
Here is an example Java main program and target Groovy script to invoke:
== TestShell.java ==
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
// call groovy expressions from Java code
Binding binding = new Binding();
binding.setVariable("input", "world");
GroovyShell shell = new GroovyShell(binding);
Object retVal = shell.evaluate(new File("hello.groovy"));
// prints "hello world"
System.out.println("x=" + binding.getVariable("x")); // 123
System.out.println("return=" + retVal); // okay
== hello.groovy ==
println "Hello $input"
x = 123 // script-scoped variables are available via the GroovyShell
return "ok"
2) Next is to use GroovyClassLoader to parse the script into a class then create an instance of it. This approach treats the Groovy script as a class and invokes methods on it as on any Java class.
GroovyClassLoader gcl = new GroovyClassLoader();
Class clazz = gcl.parseClass(new File("hello.groovy");
Object aScript = clazz.newInstance();
// probably cast the object to an interface and invoke methods on it
3) Finally, you can create GroovyScriptEngine and pass in objects as variables using binding. This runs the Groovy script as a script and passes in input using binding variables as opposed to calling explicit methods with arguments.
Note: This third option is for developers who want to embed groovy scripts into a server and have them reloaded on modification.
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
String[] roots = new String[] { "/my/groovy/script/path" };
GroovyScriptEngine gse = new GroovyScriptEngine(roots);
Binding binding = new Binding();
binding.setVariable("input", "world");
gse.run("hello.groovy", binding);
System.out.println(binding.getVariable("output"));
Note: You must include the groovy_all jar in your CLASSPATH for these approaches to work.
Reference: http://groovy.codehaus.org/Embedding+Groovy