I'm using Groovy 4.0.2 as a runtime scripting environment for my application. My goal is to introduce timeout handling into user-provided scripts, but depending on what the script is used for, different timeouts should apply. My compiler configuration looks as follows:
var config = new CompilerConfiguration();
var extensions = new ArrayList<String>()
var timedInterruptParams = new HashMap<String, Object>()
// TimedInterrupt is an annotation with multiple parameters. We're only interested
// in the "value" and the "unit" parameters. For now, let's only set the "value"...
timedInterruptParams.put("value", myTimeout);
config.addCompilationCustomizers(
new ASTTransformationCustomizer(CompileStatic.class),
new ASTTransformationCustomizer(timedInterruptParams, TimedInterrupt.class)
);
The above configuration works, but it fixes the unit
of the TimedInterrupt
to its default (which is TimeUnit.SECONDS
). I would like to overwrite this value in the timedInterruptParams
map. However, none of the following works:
timedInterruptParams.put("unit", TimeUnit.MILLISECONDS)
Produces an error during script compilation, claiming that MILLISECONDS
is not a supported constant expression.
timedInterruptParams.put("unit", new ConstantExpression(TimeUnit.MILLISECONDS)
Same as above
timedInterruptParams.put("unit", "MILLISECONDS")
Causes the groovy compiler to bug out completely with "this method should not have been called".
timedInterruptParams.put("unit", "java.util.concurrent.TimeUnit.MILLISECONDS")
Same as above
So... I'm running out of options here. Which value am I supposed to pass into the "unit"
value of the parameter map?
It took quite a bit of digging, but the answer lied within TimedInterruptibleASTTransformation
, the class which actually interprets the @TimedInterrupt
annotation. There, the unit
value was interpreted as follows:
Expression unit = node.getMember('unit') ?: propX(classX(TimeUnit), 'SECONDS')
... and propX
and classX
are static helper methods from org.codehaus.groovy.ast.tools.GeneralUtils
. With this information, it was relatively easy to make my code work. The line I was missing is:
timedInterruptParams.put("unit", propX(classX(TimeUnit.class), TimeUnit.MILLISECONDS.toString()),
It turns out that groovy interprets enum literals not as constants, but as properties of a class. This case sadly isn't covered by the auto-converter that applies to the object values of the parameter maps.