Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

Variable scopes in a ScriptRunner custom rest endpoint

Introduction

Here is something I wanted to share because I could not fine any examples or documentation about.

I have a big and complex custom rest endpoint script in ScriptRunner for Jira Data Center that's being used to import data from another system into Jira.

The issue

The script works really well until I start running those imports in parallel.
What I noticed: when importing 2 items at the same time some things get mixed up. Items are created with the issuetype of the other item or even in the target project of the other item.
I ended up with issues from a type that was not even included in the issue type scheme of that project.

My mistake was to use global variables  for the issue type and project in the script. I declared the variables outside of the rest method and provided a value inside the method. The values of those variables were "remembered" by the system and used when another request ran at the same time.

Example with issue

Here is an example script to reproduce the issue:

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import javax.ws.rs.core.MultivaluedMap
import groovy.json.JsonBuilder
import groovy.transform.Field
import javax.ws.rs.core.Response

@Field String myNumber

@BaseScript CustomEndpointDelegate delegate


testParallel( httpMethod: "GET", groups: ["jira-administrators"] ) {
    MultivaluedMap queryParams, String body -> 
    myNumber = queryParams.getFirst("myNumber") as String
    return Response.ok(new JsonBuilder([number: myNumber]).toString()).build() 
}

testParallelWait( httpMethod: "GET", groups: ["jira-administrators"] ) {
    MultivaluedMap queryParams, String body ->
    myNumber = queryParams.getFirst("myNumber") as String
    sleep(10000)
    return Response.ok(new JsonBuilder([number: myNumber]).toString()).build() 
}

myNumber is declared gloablly and gets a value in the actual methods.

  1. Call this url and do not wait for it to reply
    https://<jira-url>/rest/scriptrunner/latest/custom/testParallelWait?myNumber=7

  2. Call this url
    https://<jira-url>/rest/scriptrunner/latest/custom/testParallel?myNumber=3

  3. The first call will reply with 3 and not with 7
    --> when 1 run is already executing and another one alters a global variable during that time the 1st run will use the new value

Example without issue

Here is how to properly define the variable:

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import javax.ws.rs.core.MultivaluedMap
import groovy.json.JsonBuilder
import groovy.transform.Field
import javax.ws.rs.core.Response

@BaseScript CustomEndpointDelegate delegate


testParallel( httpMethod: "GET", groups: ["jira-administrators"] ) { MultivaluedMap queryParams, String body -> 
    String myNumber
    myNumber = queryParams.getFirst("myNumber") as String
    myNumber = getNumber(myNumber)
    return Response.ok(new JsonBuilder([number: myNumber]).toString()).build() 
}

testParallelWait( httpMethod: "GET", groups: ["jira-administrators"] ) { MultivaluedMap queryParams, String body ->
    String myNumber
    myNumber = queryParams.getFirst("myNumber") as String
    myNumber = getNumber(myNumber)
    sleep(10000)
    return Response.ok(new JsonBuilder([number: myNumber]).toString()).build() 
}

def getNumber(String myNumber){
    return "Hello ${myNumber}"
}

myNumber is declared locally in the methods.
Additionally I'm using the getNumber method where the return value is being prefixed with "Hello" to really show how the variable is not being altered by other calls to the endpoint.

Conclusion

When writing scripts we sometimes neglect the rules of coding. Thinks about your variable scopes carefully!

I hope this articles will help someone one day that struggled with this just like me.
If anyone can share the technical side of the JVM and threads related to this that would be very welcome as addition.

 

REST Endpoint documentation from Adaptavist can be found here.

2 comments

Matt Doar (Adaptavist)
Community Champion
February 7, 2025

Thanks for the reminder! If you'd like us to add some cautionary text about global variables to the docs, please would you create a support ticket at https://www.scriptrunnerhq.com/help/support with a link to this handy post.

Jacqueline Meister
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
April 14, 2025

I just wanted to add that I am experiencing the exact same behavior with variables that are initialized outside of the REST endpoints and are not global variables. I have been debugging this for the last few hours and couldn't find the problem.

I have a REST endpoint logic where multiple REST endpoints are combined in one script to present the end user with a multi-window dialog. I tried to collect all log messages during all the dialog windows/REST endpoints and send them via email at the end, but I couldn't, for the life of me, figure out why I received log messages from previous runs.

This issue should definitely be addressed in the documentation!

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events