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} )
Related
I'm trying to redefine a method at runtime using javassist, but I'm running into some issues on the last step, because of the weird requirements I have for this:
I can't require the user to add startup flags
My code will necessarily run after the class has already been defined/loaded
My code looks like this:
val cp = ClassPool.getDefault()
val clazz = cp.get("net.minecraft.world.item.ItemStack")
val method = clazz.getDeclaredMethod(
"a",
arrayOf(cp.get("net.minecraft.world.level.block.state.IBlockData"))
)
method.setBody(
"""
{
double destroySpeed = this.c().a(this, $1);
if (this.s()) {
return destroySpeed * this.t().k("DestroySpeedMultiplier");
} else {
return destroySpeed;
}
}
""".trimIndent()
)
clazz.toClass(Items::class.java)
(I'm dealing with obfuscated method references, hence the weird names)
However, calling .toClass() causes an error as there are then two duplicate classes on the class loader - and to my knowledge there's no way to unload a single class.
My next port of call to update the class was to use the attach API and an agent, but that requires a startup flag to be added (on Java 9+, I'm running J17), which I can't do given my requirements. I have the same problem trying to load an agent on startup.
I have tried patching the server's jar file itself by using .toBytecode(), but I didn't manage to write the new class file to the jar - this method sounds promising, so it's absolutely on the table to restart the server after patching the jar.
Is there any way I can get this to work with my requirements? Or is there any alternative I can use to change a method's behavior?
I am using JCommander for command line parameters parsing. I would like to add parameter dependency, but from the JCommander documentation, I am not able to find out if it is even supported. Has anyone tried it ?
For example, java -jar Some.jar -a Foo -b Hola
So, option -b shall only accompany with option -a.
This is supported in args4j. However, I can't use args4j because it does not support multiple values as JCommander.
Thanks
Yes, you can use args4j, it does support multiple values.
JCommander:
#Parameter(names={"--length", "-l"})
int length;
Args4j:
#Option(name = "-l", aliases = { "--length" })
int length;
About validation and dependency: You can do this manually, of course. It's not too much of programming. Just ignore option b if a is not given either, or throw an exception if a is non-null but b is null.
I had exactly the same problem but it looks like that args4j has added support for multiple values:
import org.kohsuke.args4j.spi.StringArrayOptionHandler;
#Option(name = "-p", handler = StringArrayOptionHandler.class, required = true)
private List<String> list;
which should allow
-p arg1 arg2 ...
I'm scratching my head on this one, The program is as follows:
class MyClass {
def static someMethod() {
def pb = new ProcessBuilder("")
pb.inheritIO()
pb.setCommand(/* command list */)
def process = pb.start()
...
println "profit"
}
}
except running the above (or the equivelant of) gives the output:
No signature of method: java.lang.ProcessBuilder.inheritIO() is applicable for argument types: () values: []
inheritIO is obviously a method defined in the ProcessBuilder class
so what is going wrong here?
CONTEXT: this is happening during a gradle build using jdk 7u55 except i imagine this info is unrelated. to me it looks like groovy has forgotten what it was doing.
EDIT: if i delete the pb.inheritIO() line then when i call pb.start() it throws another error:
java.lang.ArrayStoreException
Turns out the answer is quite involved but very specific to my setup:
although i have my org.gradle.java.home property set to the java 1.7 jdk, I am using a properties plugin that uses different property files according to an environment variable called environmentName this property was set to the incorrect value thus it was reading JAVA_HOME from my environment rather than the variable i set in the property file i wanted. I changed this and the jre was switched to the correct runtime.
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
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