NotSerializableException in jenkinsfile

I'm working on a jenkinsfile and I'm getting and exception in the third stage:

an exception which occurred:
in field com.cloudbees.groovy.cps.impl.BlockScopeEnv.locals
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@7bbae4fb
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.CaseEnv@6896a2e3
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@605ccbbc
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@7b8ef914
in field com.cloudbees.groovy.cps.Continuable.e
in object org.jenkinsci.plugins.workflow.cps.SandboxContinuable@11e73f3c
in field org.jenkinsci.plugins.workflow.cps.CpsThread.program
in object org.jenkinsci.plugins.workflow.cps.CpsThread@b2df9bb
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.threads
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@2b30596a
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@2b30596a
Caused: java.io.NotSerializableException: java.util.regex.Matcher
    at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860)
    at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65)
    at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56)

I've been reading about it and I know I can't create non-serializable variables. So, I think it has to be with this part of my code:

def artifact_name = sh (
        script: "ls -b *.jar | head -1",
        returnStdout: true
).trim()
def has_snapshot = artifact_name =~ /-TEST\.jar/
if (has_snapshot) {
    //Do something
}

My question is, how do I define that two variables in order to avoid that exception?

Upvotes: 11

Views: 20983

Answers (4)

themr97
themr97

Reputation: 1

You can use findall method, here is the code example:

def pattern = /-TEST\.jar/
def has_snapshot = artifact_name.findAll(pattern)

Upvotes: 0

Timothy G.
Timothy G.

Reputation: 9055

The accepted answer is certainly correct. In my case, I was trying to parse some JSON from an API response like so:

@NonCPS
def parseJson(rawJson) {
  return new groovy.json.JsonSlurper().parseText(rawJson)
}

All this does is return a JsonSlurper that can then be used to walk down your JSON structure, like so:

def jsonOutput = parseJson(createIssueResponse)
echo "Jira Ticket Created.  Key: ${jsonOutput.key}"

This snippet actually worked fine in my script, but later on in the script, it was using the jsonOutput.key to make a new web request. As stated in the other answer, if the script pauses when you have something stored into a local variable that cannot be serialized, you will get this exception.

When the script attempted to make the web request, it would pause (presumably because it was waiting for the request to respond), and the exception would get thrown.

In my scenario, I was able to fix this by doing this instead:

def ticketKey = parseJson(createIssueResponse).key.toString()
echo "Jira Ticket Created.  Key: ${ticketKey}"

And later on when the script attempts to send the web request, it no longer throws the exception. Now that no JsonSlurper object is present in my running script when it is paused, it works fine. I previously assumed that because the method was annotated with @NonCPS that its returned object was safe to use, but that is not true.

Upvotes: 1

Eric M.
Eric M.

Reputation: 170

Building on the accepted answer I came up with this solution:

def hello = {
    def matcher = ("Hello" =~ /Hello/)
    matcher.find()
    return matcher.group()
}.call()

I guess the stability of this is not so good, but I assume the likeliness of this failing to be very low. So if the impact of this code failing is also low it might be reasonable risk management to use this code.

The following seems to fit the case of running in a NonCPS context, but I am not 100% sure. It definitely is working though

@NonCPS def hello = {
    def matcher = ("Hello" =~ /Hello/)
    matcher.find()
    return matcher.group()
}
hello = hello.call()
println hello

Upvotes: 0

mkobit
mkobit

Reputation: 47249

Your problem is this line:

def has_snapshot = artifact_name =~ /-TEST\.jar/

The =~ is the Groovy find operator. It returns a java.util.regex.Matcher instance, which is not Serializable. If Jenkins decides to pause your script after you have stored the result in a local variable that is serialized by Jenkins that is when you get the exception. This can be easily tested by immediately adding a sleep(1) step after your invocation and watch as that same exception is thrown.

To resolve this, you should :

Upvotes: 17

Related Questions