Search code examples
xmlgradlegroovyxmlslurper

Manipulating XML in Gradle/Groovy with new 'file' node


I am trying to manipulate a wildfly 10.1. standalone.xml via gradle buildscript to modify logger settings. I have done this before, and with logger entries, this works as expected. However, now I need to add a new periodic-rotating-file-handler which in itself is not a problem, but it does act up when trying to tell what file to actually log to.

Existing code:

def configFile = {wildflyDir}/standalone/configuration/standalone.xml"
def xml = new XmlSlurper(false,false).parse(configFile)
def logging = xml.profile.subsystem.find{ it['@xmlns'] == "urn:jboss:domain:logging:3.0' }
logging.appendNode {
    logger(category:"loggingCategory") {
        level(name:"LEVEL")
    }
}

This is, as expected, highly functional. Now I need to add a snippet like this:

<periodic-rotating-file-handler>
    <formatter>
        <named-formatter name="PATTERN" />
    </formatter>
    <file path="file.log" relative-to="jboss.logging.dir" />
    <suffix value=".yyyy-MM-dd" />
    <append value="true" />
</periodic-rotating-file-handler>

The problem exists in the file definition, as that one would look like this in the build.gradle file:

file(path:"file.log" "relative-to":"jboss.logging.dir")

And this is being interpreted by gradle as new File(arg1, arg2), so basically it is trying to add a file-Object with the given parameters to the XML.

This is valid, but definitely not what I want, since I just need the corresponding XML Node. I tried escaping in multiple ways:

  • escaped file with single quotes, double quotes, triple quotes, made it a slashy and a dollarslashy string -> Tried to create a File object
  • escaped the whole line with those same options -> line is not present in XML
  • tried adding node without parentheses -> tried to create File object
  • tried adding as extra closure -> tried to create File object
  • tried extracting the node as text, parsing is with XMLSlurper and addind that to the XML -> snippet not present in XML

There have been a few more things I tried, but I cannot recall them due to frustration.

My final attempt was to try and just add an empty file node to the XML, however when using file() gradle did not know which File-constructor to use, and when I used file I got an error: Namespace prefix: file is not bound to a URI

If anyone has any idea how to properly escape file or has another way of adding said file-node to the XML please let me know.

Thank you.


Solution

  • (1) call builder functions on delegate:

    //just declare `file` to emulate problem in usual groovy console 
    def file={String a, String b-> println "file: a=$a, b=$b"}
    
    def xml = new XmlSlurper(false,false).parseText('''
        <root>
            <profile>
                <subsystem xmlns="urn:jboss:domain:logging:3.0"/>
            </profile>
        </root>''')
    
    def logging = xml.profile.subsystem.find{ it['@xmlns'] == "urn:jboss:domain:logging:3.0" }
    
    logging.plus{
        logger(category:"loggingCategory") {
            level(name:"LEVEL")
        }
        //be specific that you want to call file function on delegate and not on owner (by default)
        delegate.'file'(path:"file.log", "relative-to":"jboss.logging.dir")
        //or this also works:
        "${'file'}"(path:"file.log", "relative-to":"jboss.logging.dir")
    
    }
    
    println groovy.xml.XmlUtil.serialize(xml)
    

    (2) workaround with XmlParser :

    use current as accessor to current parent node

    def xml = new XmlParser(false,false).parseText('''
        <root>
            <profile>
                <subsystem xmlns="urn:jboss:domain:logging:3.0"/>
            </profile>
        </root>''')
    
    def logging = xml.profile.subsystem.find{ it['@xmlns'] == "urn:jboss:domain:logging:3.0" }
    
    logging.plus{
        logger(category:"loggingCategory") {
            level(name:"LEVEL")
        }
        current.appendNode("file", [path:"file.log", "relative-to":"jboss.logging.dir"])
    }
    
    println groovy.xml.XmlUtil.serialize(xml)