Search code examples
jenkinsgroovyjenkins-pipelineniopipeline

Jenkins scripted Pipeline: How to apply @NonCPS annotation in this specific case


I am working on a scripted Jenkins-Pipeline that needs to write a String with a certain encoding to a file as in the following example:

class Logger implements Closeable {

    private final PrintWriter writer

    [...]

    Logger() {
        FileWriter fw = new FileWriter(file, true)
        BufferedWriter bw = new BufferedWriter(fw)
        this.writer = new PrintWriter(bw)
    }

    def log(String msg) {
        try {
            writer.println(msg)
            [...]
        } catch (e) {
            [...]
        }
    }
}

The above code doesn't work since PrintWriter ist not serializable so I know I got to prevent some of the code from being CPS-transformed. I don't have an idea on how to do so, though, since as far as I know the @NonCPS annotation can only be applied to methods. I know that one solution would be to move all output-related code to log(msg) and annotate the method but this way I would have to create a new writer every time the method gets called.

Does someone have an idea on how I could fix my code instead?

Thanks in advance!


Solution

  • Here is a way to make this work using a log function that is defined in a shared library in vars\log.groovy:

    import java.io.FileWriter
    import java.io.BufferedWriter
    import java.io.PrintWriter
    
    // The annotated variable will become a private field of the script class. 
    @groovy.transform.Field 
    PrintWriter writer = null
    
    void call( String msg ) {
        if( ! writer ) {
            def fw = new FileWriter(file, true)
            def bw = new BufferedWriter(fw)
            writer = new PrintWriter(bw)
        }
    
        try {
            writer.println(msg)
            [...]
        } catch (e) {
            [...]
        }     
    }
    

    After all, scripts in the vars folder are instanciated as singleton classes, which is perfectly suited for a logger. This works even without @NonCPS annotation.

    Usage in pipeline is as simple as:

    log 'some message'