how to create xml document with special node names with groovy markupbuilder - groovy

I am building an ant script with groovy markupbuilder. Unfortunately markupbuilder doesn't allow me to create nodes with name 'target' (no problem with targetee),
becauase it throws me
Caught: groovy.lang.MissingMethodException: No signature of method: java.lang.String.call() is applicable for argument types: (java.util.LinkedHashMap, BuildGen$_main_closure1_closure5) values: [[name:clean], BuildGen$_main_closure1_closure5#18efaea]
Possible solutions: wait(), any(), trim(), split(), dump(), next()
so inside my markupbuilder this snippet works:
targete(name: 'clean') {
delete(dir: rootProj.compilerOutput)
}
but I would like to achieve the same with a 'target' node..
I managed to create an empty 'target' node this way:
builder.invokeMethod('target', [name: 'clean'])
but how can I go on and put more nodes inside this 'target' node?
Example of working code:
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(writer)
builder.project(name: projectName, basedir:'.') {
// works with 'target2' but not with 'target'
'target2'(name: 'build-subprojects') {
rootProj.getAllDependentProjects().each { p->
echo(message: "Compiling project: ${p.projectName}")
// some real stuff
}
}

If I guess right, your problem is you want to create nodes with names that are Groovy keywords?
If so, then you can simply put the name in quotes, like so:
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder( writer )
builder.project {
'for'(name: 'clean') {
delete(dir: '.')
}
}
println writer
That snippet will print out:
<project>
<for name='clean'>
<delete dir='.' />
</for>
</project>
For me, this works:
def projects = [ 'pro_one', 'pro_two' ]
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(writer)
builder.project( name: 'test', basedir:'.' ) {
'target'( name: 'build-subprojects' ) {
projects.each { p ->
echo( message: "Compiling project: ${p}" )
}
}
}
println writer.toString()
Have you got target set to anything in your code before calling this?
You could try:
builder.target( name: 'build-subprojects' ) {
That might work better?
I've tried Groovy 1.7.5, and 1.8 beta 2 and can't get it to fail :-/

Related

Object Array Declaration in Groovy

How come I cannot declare an array of People in Groovy as shown.
Maybe I'm lacking the deeper understanding of classes
class People {
Integer id
}
class Job {
def func() {
People[] p = new People[10]
}
}
I get an error of People[] cannot be applied to app.People[]
The code sample you have shown does not reproduce the error you mentioned in the question above. It's broken actually and does not compile - method func() is missing its body. If you correct the code to e.g.
class People {
Integer id
}
class Job {
def func() {
People[] p = new People[10]
assert p.size() == 10
println p
}
}
new Job().func()​
you will see it produces the expected result - check it out in the Groovy web console here. When you run it you will see following output to the console:
[null, null, null, null, null, null, null, null, null, null]
The difference between Groovy and Java
When it comes to array initialization there is one significant difference between Groovy and Java. In Java you can initialize an array of People[] like this:
People[] p = new People[] { new People(), new People(), /* ... */ new People() };
It wont work in Groovy, because Groovy reserves {} for closures. In Groovy you can initialize such array as:
People[] p = [new People(), new People(), new People()] as People[]
While Szymon Stepniak's answer is correct for Groovy 2.5 and below, Java-style array initialization are part of the enhancements of Groovy 3.0 and 2.6 made possible by the new parrot parser.
Example from the release notes:
def primes = new int[] {2, 3, 5, 7, 11}
assert primes.size() == 5 && primes.sum() == 28
assert primes.class.name == '[I'
def pets = new String[] {'cat', 'dog'}
assert pets.size() == 2 && pets.sum() == 'catdog'
assert pets.class.name == '[Ljava.lang.String;'
// traditional Groovy alternative still supported
String[] groovyBooks = [ 'Groovy in Action', 'Making Java Groovy' ]
assert groovyBooks.every{ it.contains('Groovy') }
Szymon Stepniak's answer is correct. I'll point another example of a real case in some unit test that I've worked (general Object type):
Object[] o = [YourModel] as Object[]
This is sufficient to mock a general Object with your model properties.

How can I retrieve the build parameters from a queued job?

I would like to write a system groovy script which inspects the queued jobs in Jenkins, and extracts the build parameters (and build cause as a bonus) supplied as the job was scheduled. Ideas?
Specifically:
def q = Jenkins.instance.queue
q.items.each { println it.task.name }
retrieves the queued items. I can't for the life of me figure out where the build parameters live.
The closest I am getting is this:
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
it.task.properties.each { key, val ->
println(" ${key}=${val}")
}
}
This gets me this:
4.1.next-build-launcher:
com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty$ScannerJobPropertyDescriptor#b299407=com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty#5e04bfd7
com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty$DescriptorImpl#40d04eaa=com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty#16b308db
hudson.model.ParametersDefinitionProperty$DescriptorImpl#b744c43=hudson.mod el.ParametersDefinitionProperty#440a6d81
...
The params property of the queue element itself contains a string with the parameters in a property file format -- key=value with multiple parameters separated by newlines.
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
println("Parameters: ${it.params}")
}
yields:
dbacher params:
Parameters:
MyParameter=Hello world
BoolParameter=true
I'm no Groovy expert, but when exploring the Jenkins scripting interface, I've found the following functions to be very helpful:
def showProps(inst, prefix="Properties:") {
println prefix
for (prop in inst.properties) {
def pc = ""
if (prop.value != null) {
pc = prop.value.class
}
println(" $prop.key : $prop.value ($pc)")
}
}
def showMethods(inst, prefix="Methods:") {
println prefix
inst.metaClass.methods.name.unique().each {
println " $it"
}
}
The showProps function reveals that the queue element has another property named causes that you'll need to do some more decoding on:
causes : [hudson.model.Cause$UserIdCause#56af8f1c] (class java.util.Collections$UnmodifiableRandomAccessList)

Groovy XML parsing (HTML slurping), can't get my specific case to work

Okay, here's what I'm looking for.
I want to go into the DOM and look for an <a id> starting with "thread_title_". Here are a couple things I've tried:
// setup
def slurper = new XmlSlurper(new org.ccil.cowan.tagsoup.Parser())
def gurl = new URL("url")
gurl.withReader { gReader ->
def try1 = gHTML.body.find { it['#id'].startsWith("thread_title_") }
// fails: Caught: groovy.lang.MissingMethodException: No signature of method: groovy.util.slurpersupport.Attributes.startsWith() is applicable for argument types: (java.lang.String) values: [thread_title_]
def try2 = gHTML.body.find { it['#id'] =~ /thread_title_/ }
// fails: Caught: groovy.lang.MissingMethodException: No signature of method: groovy.util.slurpersupport.Attributes.startsWith() is applicable for argument types: (java.lang.String) values: [thread_title_]
def try3 = gHTML.body.find { it['#id'].name.startsWith("thread_title_") }
// fails: Caught: groovy.lang.MissingMethodException: No signature of method: groovy.util.slurpersupport.NodeChildren.startsWith() is applicable for argument types: (java.lang.String) values: [thread_title_]
def try4 = gHTML.body.find { it['#id'] == 'thread_title_745429' }
// doesn't fail, but doesn't return anything either
def try5 = gHTML.body.findAll { it.name() == 'a' && it.#id.startsWith('thread_title_') }
try5.eachWithIndex { row, i ->
println "rn: $i"
}
// no output
}
Here is the gdoc for Attributes. I don't really want "name", I want "value". The gpath page implies that node.character.find { it['#id'] == '2' } works, which seems much like find..startsWith to me. This stackoverflow answer is similar, but the startsWith is different and seems to throw a wrench into the whole thing. The fifth entry was inspired by this stackoverflow answer.
And if you are concerned it's a problem with the input data:
$ curl --silent http://www.advrider.com/forums/forumdisplay.php?f=18 | grep thread_title | wc -l
43
Here is some sample output, using the curl | grep above.
text
text
I have Groovy 1.7.10 installed. I could go newer, don't know if it would help.
How this?
#Grab( 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' )
import org.ccil.cowan.tagsoup.Parser
def gHTML = new URL( 'http://www.advrider.com/forums/forumdisplay.php?f=18' ).withReader { r ->
new XmlSlurper( new Parser() ).parse( r )
}
def allLinks = gHTML.body.'**'.findAll { it.name() == 'a' && it.#id.text().startsWith( 'thread_title_' ) }
allLinks.each { link ->
println "${link.text()} -> ${link.#href}"
}
Let me know if you have any problems or questions :-)

Load script from groovy script

File1.groovy
def method() {
println "test"
}
File2.groovy
method()
I want to load/include the functions/methods from File1.groovy during runtime, equals to rubys/rake's load. They are in two different directories.
If you don't mind the code in file2 being in a with block, you can do:
new GroovyShell().parse( new File( 'file1.groovy' ) ).with {
method()
}
Another possible method would be to change file1.groovy to:
class File1 {
def method() {
println "test"
}
}
And then in file2.groovy you can use mixin to add the methods from file1
def script = new GroovyScriptEngine( '.' ).with {
loadScriptByName( 'file1.groovy' )
}
this.metaClass.mixin script
method()
You can evaluate any expression or script in Groovy using the GroovyShell.
File2.groovy
GroovyShell shell = new GroovyShell()
def script = shell.parse(new File('/path/file1.groovy'))
script.method()
It will be easiest if file1.groovy is an actual class class File1 {...}.
Given that, another way to do it is to load the file into the GroovyClassLoader:
this.class.classLoader.parseClass("src/File1.groovy")
File1.method()
File1.newInstance().anotherMethod()
I am late on this but. This is how we have been achieving what you were asking. So, i have a file1.gsh like so:
File1:
println("this is a test script")
def Sometask(param1, param2, param3)
{
retry(3){
try{
///some code that uses the param
}
catch (error){
println("Exception throw, will retry...")
sleep 30
errorHandler.call(error)
}
}
}
return this;
And in the other file, these functions can be accessed by instantiating first. So in file2.
File2:
def somename
somename = load 'path/to/file1.groovy'
//the you can call the function in file1 as
somename.Sometask(param1, param2, param3)
Here is what I'm using.
1: Write any_path_to_the_script.groovy as a class
2: In the calling script, use:
def myClass = this.class.classLoader.parseClass(new File("any_path_to_the_script.groovy"))
myClass.staticMethod()
It's working in the Jenkins Groovy script console. I have not tried non-static methods.
The answer by #tim_yates that uses metaClass.mixin should have worked without needing any changes to file1.groovy (i.e., mixin with the script object), but unfortunately there is a bug in metaClass.mixin that causes a SO error in this scenario (see GROOVY-4214 on this specific issue). However, I worked around the bug using the below selective mixin:
def loadScript(def scriptFile) {
def script = new GroovyShell().parse(new File(scriptFile))
script.metaClass.methods.each {
if (it.declaringClass.getTheClass() == script.class && ! it.name.contains('$') && it.name != 'main' && it.name != 'run') {
this.metaClass."$it.name" = script.&"$it.name"
}
}
}
loadScript('File1.groovy')
method()
The above solution works with no changes being needed to File1.groovy or the callers in File2.groovy (except for the need to introduce a call to loadScript function).

What's wrong with this Groovy construct?

This is a short Groovy script:
import org.apache.commons.io.FileUtils;
def dir = new File("/mydir")
def files = FileUtils.listFiles(dir, new String[] { "java" }, false)
It says:
No expression for the array constructor call at line: 2
What's wrong?
The call should be:
def files = FileUtils.listFiles(dir, [ "java" ] as String[], false)
Groovy uses Lists by default, and the as operator can be used to coerce these lists into arrays of a specified type (often for interacting with the java api as in this example)
[edit]
As an aside, you can do this with pure Groovy like so:
def files = dir.listFiles().findAll { it.name ==~ /.*\.java/ }
Then, you don't need Commons FileUtils

Resources