Groovy JsonBuilder call methods to add information - groovy

The following code works fine
def json = new JsonBuilder()
json {
writeNumbers(delegate, "myNumbers")
}
println json.toPrettyString()
def writeNumbers(json, name) {
json."$name" {
"w" 32
"h" 32
}
}
But as soon as I move or add a writeNumbers call inside of another 'scope', I get a stackoverflow exception; just like so
def json = new JsonBuilder()
json {
scopes {
writeNumbers(delegate, "myNumbers")
}
}
println json.toPrettyString()
def writeNumbers(json, name) {
json."$name" {
"w" 32
"h" 32
}
}
Result:
Caught: java.lang.StackOverflowError
java.lang.StackOverflowError
Why is this happening and how can I get around it?
Thanks

I think this is caused by the underlying error that the method writeNumbers is unknown when building the chain of closures.
You need to change:
writeNumbers(delegate, "myNumbers")
to
this.writeNumbers(delegate, "myNumbers")
And it will work... Interesting though, this feels like a bug... I'll investigate if I get some free time ;-)
Edit: Found this previous question which shows the same thing

Related

Compile error when using modulus operator in MarkupTemplateEngine

I have been using the Groovy template engine without issue for about a year, but just ran into a strange issue and I'm not sure if I'm crazy, or this is expected behavior.
All things being equal, using this function throws a compile error when used inside a Groovy template.
def isEven(n) { n % 2 == 0 }
General error during class generation: org.codehaus.groovy.ast.Parameter cannot be cast to org.codehaus.groovy.ast.expr.VariableExpression
But if I do it this way...
def isEven(n) { n.mod(2) == 0 }
...everything works as expected.
I didn't notice any special case in the docs, but I could have missed it. Could anyone offer some insight into the meaning behind this error, and perhaps offer some guidance to avoid pitfalls like this in the future?
Cheers, and thank you!
Groovy version 2.5
Update
Here is a full example template that throws the error.
def isEven(n) { n % 2 == 0 }
def items = ["Zero", "One", "Two"]
yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
body {
ul {
items.eachWithIndex { item, i ->
if(isEven(i)) li(item)
}
}
}
}
And here is an example of how the template is rendered.
TemplateConfiguration config = new TemplateConfiguration();
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
Template template = engine.createTemplate(new File('test.tpl').newReader());
Map<String, Object> model = new HashMap<>();
Writable output = template.make(model);
output.writeTo(new File('test.html').newWriter());
Changing the isEven method to use Number.mod compiles just fine.

SpockExecutionException: Data provider has no data

I've done a bunch of searching and, while I've found a few hits, like Why does Spock think my Data Provider has no data?, none of them seem to be very helpful.
I've only done a data provider a couple of times, but it seems perfect for this. I have the following static method:
static List<ContactPointType> getAddressTypes() {
List<ContactPointType> result = new ArrayList<>();
for (ContactPointType cpType : ContactPointType.values()) {
if (cpType.toString().endsWith("Addr")) {
result.add(cpType);
}
}
return result;
}
And then I'm trying to use it as a data provider for calling a function on my class:
#Unroll("#cpType should be address")
def "isAddress addresses"() {
expect: "isAddress() to be true"
contactPoint.isAddress(cpType)
where:
cpType << getAddressTypes()
}
When I run this, I get:
org.spockframework.runtime.SpockExecutionException: Data provider has no data
at org.spockframework.runtime.JUnitSupervisor.afterFeature(JUnitSupervisor.java:191)
at org.spockframework.runtime.BaseSpecRunner.runFeature(BaseSpecRunner.java:236)
Like I said, it seems pretty straightforward. Does anyone have any ideas?
Well, I've tried the data provider feature and it works as expected:
#Unroll("max(1, #cpType) == #cpType")
class MyFirstSpec extends Specification {
def "let's try this!"() {
expect:
Math.max(1, cpType) == cpType
where:
cpType << dataProvider()
}
List<Integer> dataProvider() {
[2,3,4]
}
}
However if I rewrite the dataProvider function like this, I see the exception that you've mentioned:
List<Integer> dataProvider() {
[] // returns an empty list
}
Yields:
org.spockframework.runtime.SpockExecutionException: Data provider has no data
at org.spockframework.runtime.JUnitSupervisor.afterFeature(JUnitSupervisor.java:180)
at org.spockframework.runtime.BaseSpecRunner.runFeature(BaseSpecRunner.java:239)
So my idea is that probably you end up with an empty list in the data provider implementation and that's why it doesn't work
Another possible (although slightly less realistic idea to be honest) is that you've messed something up with Groovy/Java interconnection
So in terms of resolution to wrap up:
Try to use some more straightforward data provider implementation and test it
If it doesn't work - just define data provider like me in Groovy and re-test

Groovy collect returning GString in a Jenkins Workflow script

It seems that in the following piece of code:
def formattedPaths = affectedFiles.collect {
"${it.editType.name} ${it.path}"
}
at least sometimes formattedPaths evaluates to a GString instead of a List. This piece of code is a fragment of a larger Jenkins Workflow script, something like:
node {
currentBuild.rawBuild.changeSets[0].collect {
"""<b>${it.user}</b> # rev. ${it.revision}: ${it.msg}
${affectedFilesLog(it.affectedFiles)}"""
}
}
def affectedFilesLog(affectedFiles) {
println "Affected files [${affectedFiles.class}]: $affectedFiles"
def formattedPaths = affectedFiles.collect {
"${it.editType.name} ${it.path}"
}
println "formattedPaths [${formattedPaths.class}]: $formattedPaths"
formatItemList(formattedPaths)
}
def formatItemList(list) {
if (list) {
return list.join('\n')
}
return '(none)'
}
Running this script in Jenkins produces output:
Running: Print Message
Affected files [class java.util.ArrayList]: [hudson.scm.SubversionChangeLogSet$Path#5030a7d8]
Running: Print Message
formattedPaths [class org.codehaus.groovy.runtime.GStringImpl]: edit my/path/flow.groovy
(...)
groovy.lang.MissingMethodException: No signature of method: java.lang.String.join() is applicable for argument types: (java.lang.String) values: [
]
And this makes me believe that in the code:
println "Affected files [${affectedFiles.class}]: $affectedFiles"
def formattedPaths = affectedFiles.collect {
"${it.editType.name} ${it.path}"
}
println "formattedPaths [${formattedPaths.class}]: $formattedPaths"
affectedFiles is ArrayList (script output Affected files [class java.util.ArrayList]: [hudson.scm.SubversionChangeLogSet$Path#5030a7d8] in the output)
but result of running collect method on it - assigned to formattedPaths - is a GString (output: formattedPaths [class org.codehaus.groovy.runtime.GStringImpl]: edit my/path/flow.groovy)
Shouldn't the collect method always return a List?
After the discussion in the comments pointing that it may be some side effects done by the Jenkins Workflow plugin, I decided to use a plain for-each loop:
def affectedFilesLog(affectedFiles) {
println "Affected files [${affectedFiles.class}]: $affectedFiles"
def ret = ""
for (Object affectedFile : affectedFiles) {
ret += affectedFile.path + '\n'
}
println("affectedFilesLog ret [${ret.class}]: $ret")
if (!ret) {
return '(brak)'
}
return ret;
}
EDIT 19/11/2015:
Jenkins workflow plugin mishandles functions taking Closures, see https://issues.jenkins-ci.org/browse/JENKINS-26481 and its duplicates. So rewriting the code to a plain Java for-each loop was the best solution.
You cannot currently use the collect method. JENKINS-26481
I guess your code is not thread safe. If you pass some objects as paramters to some other functions, do not change this. Always create and return a new changed object. Do not manipulate the original data. You should check where your objects live. Is it just inside a function or in global scope?

Code works in shell but not as a script - MissingPropertyException

I'm learning Groovy and came across this curious behavior. Executing the following code in groovysh works without a problem, but saving it as 'foo.groovy' and running it via groovy foo.groovy results in a MissingPropertyException: No such property: a for class: foo in line 3:
def a(n) { n*n }
def x(m) { m(2) }
x(a)
Changing a to a closure a = { n -> n*n } makes this error go away, but I'd like to avoid that for my real use case which is more complex. I've also found this question which deals with the same problem inside of a class, but the solution using this.& isn't applicable as my method isn't inside of a class, and coming from a python background I'd like to keep it that way.
What am I doing wrong? And where is this behavior documented?
It looks like groovysh stores:
def a(n) { n*n }
as a MethodClosure into a binding variable called a
This will not happen outside of groovysh, and as you have seen you'll need to define a as a closure:
def a = { n -> n*n }
def x( m ) { m( 2 ) }
x(a)
Or use the method reference operator &:
def a(n) { n*n }
def x(m) { m(2) }
x( this.&a )

How do I print a Groovy stack trace?

How do I print a Groovy stack trace? The Java method, Thread.currentThread().getStackTrace() produces a huge stack trace, including a lot of the Groovy internals. I'm seeing a function called twice from a StreamingMarkupBuilder that looks like it should only be called once and I would like to see why Groovy thinks it should be calling it twice.
Solution:
org.codehaus.groovy.runtime.StackTraceUtils.sanitize(new Exception()).printStackTrace()
Original answer:
A Google search returns the following information:
Apparently, there is a method in org.codehaus.groovy.runtime.StackTraceUtils called printSanitizedStackTrace. There isn't much documentation for the method, though there is a method called sanitize which is described as
remove all apparently groovy-internal
trace entries from the exception
instance This modifies the original
instance and returns it, it does not
clone
So I would try org.codehaus.groovy.runtime.StackTraceUtils.printSanitizedStackTrace(Throwable t) (it is static)
and see if that works for you.
I found this questions when searching for "spock print full stack trace".
My unit tests are written in Groovy, using the Spock testing framework and they're run in the context of a Gradle build.
The fix for me was as simple as adding exceptionFormat = 'full' to my Gradle test task specification:
test {
testLogging {
exceptionFormat = 'full'
}
}
I have designed this simple code for stack trace printing, based on artificial simulation of a NullPointerException.
This code produces the same output in both modes: from a Jenkinsfile (Pipeline) and from a normal .groovy script in a command line.
def getStackTrace() {
try {
null.class.toString() // simulate NPE
} catch (NullPointerException e) {
return e.getStackTrace()
}
return null
}
def printStackTrace() {
def stackTraceStr = ""
def callingFuncFound = false
for (StackTraceElement ste : getStackTrace()) {
if (callingFuncFound) {
stackTraceStr += ste.toString() + '\n'
}
if (!callingFuncFound && ste.toString().startsWith(this.class.name + '.printStackTrace(')) {
callingFuncFound = true
}
}
println(stackTraceStr)
}
Some explanations:
The output is concatenated into a single string to avoid being mixed with "[Pipeline] echo" message prefix of Jenkins Pipeline's println()).
The number of "unnecessary" upper stack trace elements related to the NPE is different in Jenkinsfile and in a normal command line. This is why I calculate callingFuncFound and don't use just something like e.getStackTrace()[2..-1] to skip them.

Resources