| I am getting an exception when attempting to serialize a groovy Map that I've generated:
an exception which occurred:
in field groovy.lang.Closure.delegate
in object Script1$_run_closure1@30d6aac5
in field com.nike.acid.pipeline.configuration.adaptors.AdvancedClientAdaptor.clientConverter
in object com.nike.acid.pipeline.configuration.adaptors.AdvancedClientAdaptor@629f71ba
in field com.cloudbees.groovy.cps.impl.BlockScopeEnv.locals
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@2c17fa09
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@1635b321
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@146f27c2
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@19d1436
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@1d8bbdc9
in field com.cloudbees.groovy.cps.Continuable.e
in object org.jenkinsci.plugins.workflow.cps.SandboxContinuable@39c61bdb
in field org.jenkinsci.plugins.workflow.cps.CpsThread.program
in object org.jenkinsci.plugins.workflow.cps.CpsThread@7ebbe670
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.threads
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@3b2aa9f8
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@3b2aa9f8
Caused: java.io.NotSerializableException: Script1
The problem here is, I'm not asking AdvancedClientAdaptor to persist the clientConverter field (It has Closures in it). I'm asking it to persist a JsonStringified groovy map. The issue appears to be in the AdvanceClientAdaptor class I've written. It accepts groovy map that it performs transformations on. Here is a trivial example:
def Map = [
targetConfig: [
foo: "bar"
],
clientConverter: [
foo: { targetConfig, value -> targetConfig['foo'] = value
}]
]
The AdvancedClientConverter class basically accepts this Map as an input, assigns 2 member property variables to each of the 2 map fields above:
class AdvancedClientConverter implements Serializable {
Map targetConfig
Map clientConverter
def steps //running groovy instance
AdvancedClientConverter(Map converterMap, def steps) {
this.clientConverter = converterMap.clientConverter
this.targetConfig = converterMap.targetConfig
}
def converterOperation() {
this.targetConfig.foo = this.clientConverter.foo(this.targetConfig, "baz");
String fileString = JsonOutput.prettyPrint(JsonOutput.toJson(targetConfig))}
steps.echo ("final config: ${fileString}")
steps.writeFile file: "FinalConfig.text", text: fileString
return target
}
}
The above is all invoked in a running Jenkins Pipeline:
#!groovy
@Library(['pipeline-library-with-advanced-client-adaptor']) _
def config = []
def converterMap = //converter map listed above
node {
def advancedClientAdaptor = new AdvancedClientAdaptor(this, converterMap)
//Failure:
advancedClientAdaptor.converterOperation()
}
This will throw the above exception. The actual object persisted however, looks accurate. The echo step prior to the writeFile call produces:
Yet the exception is thrown on a Completely different field that I am not persisting Even though, those values entered this class as part of the same map, they are assigned as separate member variables and presumably, different memory addresses. The String being persisted appears sanitary (No closures or other Non-serializable entities) This does not occur in JVM (Java 11) running Groovy 1.5.8 performing File.write commands. The writeFile operation succeeds if using the @NonCPS annotation (but @NonCPS breaks other functionality of the class) Please let me know if you need additional details or a working sample app that reproduces this issue. |