Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Scriptrunner post-function script failing intermittently on defect creations - NullPointerException

djohnsoncainc May 4, 2020

Hi Adaptavist Gurus,

We created a custom post-function Groovy script a little over a year ago that we use in certain project defect transition workflows.

The script was working consistently to use 2 custom field values to set the defect priority.

It now is failing intermittently on defect creation only. I have tried to reproduce it consistently. On our test server I modified the script version to try to add some logging to help me debug the issue, but I am not a coder and what I added and how/where I did it did end up being helpful. 

The failure rate of the script is higher now than it was before our recent upgrade and is project specific. Some projects it very rarely fails while others, it is failing more than it succeeds.

The update defect post function code is very similar, and it does not ever fail.

Here is a runtime error for a specific issue (defect) and project where the script failed:

2020-05-01 09:59:03,945 WARN [workflow.AbstractScriptWorkflowFunction]: Not able to get priority for new defect. Leaving priority not set (default).
2020-05-01 09:59:03,946 ERROR [workflow.AbstractScriptWorkflowFunction]: *************************************************************************************
2020-05-01 09:59:03,946 ERROR [workflow.AbstractScriptWorkflowFunction]: Script function failed on issue: DFP-3889, actionId: 1, file: /data/jira-home/scripts/CreatePriorityPostScriptPOCv2.groovy
java.lang.NullPointerException: Cannot get property 'id' on null object
	at CreatePriorityPostScriptPOCv2.run(CreatePriorityPostScriptPOCv2.groovy:94)


Here is payload:

{
    "full.module.key": "com.onresolve.jira.groovy.groovyrunnerrungroovy-function (java.lang.String)",
    "canned-script": "com.onresolve.scriptrunner.canned.jira.workflow.postfunctions.CustomScriptFunction (java.lang.String)",
    "class.name": "com.onresolve.jira.groovy.GroovyFunctionPlugin (java.lang.String)",
    "issue": "DFP-3889 (com.atlassian.jira.issue.IssueImpl)",
    "passesCondition": "true (java.lang.Boolean)",
    "transientVars": {
        "entry": "com.opensymphony.workflow.spi.SimpleWorkflowEntry@53a846f6",
        "issue": "DFP-3889 (com.atlassian.jira.issue.IssueImpl)",
        "configuration": "com.opensymphony.workflow.config.DefaultConfiguration@793ce9de",
        "context": "com.opensymphony.workflow.basic.BasicWorkflowContext@43305764",
        "createdStep": "SimpleStep@2[owner=, actionId=0, status=open] (com.opensymphony.workflow.spi.SimpleStep)",
        "originalissueobject": "null (org.codehaus.groovy.runtime.NullObject)",
        "actionId": "1 (java.lang.Integer)",
        "currentSteps": "[SimpleStep@2[owner=, actionId=0, status=open]] (java.util.ArrayList)",
        "store": "com.opensymphony.workflow.spi.ofbiz.OfbizWorkflowStore@db4af2a",
        "descriptor": "com.atlassian.jira.workflow.ImmutableWorkflowDescriptor@348cd251"
    },
    "log": "org.apache.log4j.Logger@2e44aced"
}



Below is the script: Line 94 which is the supposed culprit is in bold. Any help greatly appreciated!

// Set a priority using Post-function script on defect create
// Changes the priority of an issue depending on 2 other custom fields (severity and business impact ("bizimpact") with Script Runner
// Map this to issue creation transition - needs to be LAST post-function
// useful reference: https://community.atlassian.com/t5/Answers-Developer-Questions/How-to-change-issue-priority-with-script-runner-groovy/qaq-p/478700
// last updated: 1/25/2019 based on pilot feedback

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.comments.CommentManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.priority.Priority;
import com.atlassian.jira.issue.IssueInputParametersImpl;
import com.atlassian.jira.issue.customfields.option.Option;


// Get the current Issue
Issue issue = issue

// Get the Manager classes required
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def commentManager = ComponentAccessor.getCommentManager()
def constantsManager = ComponentAccessor.getConstantsManager()
def issueService = ComponentAccessor.getIssueService()


// Get a pointer to the current logged in user
def user = ComponentAccessor.getJiraAuthenticationContext().getUser()


// Get objects and values for relevant fields associated with the specific issue
// Found you have to explicitly cast the field values as strings for script to work

def bizimpactObject = customFieldManager.getCustomFieldObject("customfield_13402")
def severityObject = customFieldManager.getCustomFieldObject("customfield_13402")

def bizimpact = issue.getCustomFieldValue(customFieldManager.getCustomFieldObject("customfield_13402"));
bizimpact = bizimpact.toString()
def severity = issue.getCustomFieldValue(customFieldManager.getCustomFieldObject("customfield_10201"));
severity = severity.toString()

// for debugging get current issue priority values to compare
// def OrigPriority = issue.getPriorityObject().getName()
// OrigPriority = OrigPriority.toString()
// def OrigPriorityID = issue.getPriorityObject().getId()


// use priorityMatrix below as map. Has linked pair value combinations of severity, and business impact values to priority keeping code in for doc purposes only in this version
// Key at far left is severity and second dimension key is the bizimpact. These values as keys and function returns priority name (in quotes)

def priorityMatrix =
[
Blocker: [Highest: "P0 - Urgent", Significant: "P1 - High" , Average: "P1 - High", Minimal: "P2 - Medium-High"],
Major: [Highest: "P1 - High", Significant: "P2 - Medium-High", Average: "P3 - Medium-Low", Minimal: "P4 - Low"],
Minor: [Highest:"P2 - Medium-High" , Significant: "P3 - Medium-Low" , Average: "P4 - Low", Minimal: "P4 - Low"],
Trivial: [Highest:"P3 - Medium-Low" , Significant: "P3 - Medium-Low" , Average: "P4 - Low", Minimal: "P4 - Low"],
]

// use map lookup method to calculate priority and leave default value of Not Set if map is broken
def CalcPriority = "Not Set"
if (priorityMatrix.containsKey(severity) && priorityMatrix.find{bizimpact} != null)
{
CalcPriority = priorityMatrix[severity][bizimpact]
}
else {
log.warn "Business or Severity field selected not in matrix."
}
CalcPriority = CalcPriority.toString()
// once you have looked up the calculated priority name in the matrix, get the associated priority object based on the name

def CalcPriorityObject = constantsManager.priorityObjects.findByName(CalcPriority)

// initialize some messages with default values
def msg1 = "No Special Message."
def errmsg = "No Errors."

// check lookup of priority by name
if (CalcPriorityObject)
{
// msg1 = "Valid priority object found."
log.debug("Able to find priority based on lookup.")
}
else{
// msg1 = "Priority object not found, so leaving priority at current value."
log.warn("Not able to get priority for new defect. Leaving priority not set (default).")
}

// for debugging create a string you can write to issue comments
// def stringbody = "Current Values of Business Impact, Severity, Original Priority, Original Priority ID, New Derived Priority, New Priority ID: \n" + bizimpact + ", " + severity + ", " + OrigPriority + ", " + CalcPriority.toString() + ", " + OrigPriorityID.toString() + ", " + msg1.toString()

// this is start of code to actually set field value based on id of the Calculated priority value on the create transition only.
// first create object to hold all parameters to pass into the issue service for new issue creation
def issueInputParameters = new IssueInputParametersImpl();

issueInputParameters.setPriorityId(CalcPriorityObject.id);
issueInputParameters.setSkipScreenCheck(true);

// validate object

def validationResult = issueService.validateUpdate(user, issue.id, issueInputParameters);

if (validationResult.isValid())
{
// actually update the issue and store to db
issueService.update(user, validationResult)


// next section is for debugging - writes to comments
// errmsg = "validation succeeded. check priority. should be updated. " + issueInputParameters
// stringbody = stringbody + ", " + errmsg.toString()
// commentManager.create(issue,user,stringbody,true)
}
else {
// log warning
log.warn validationResult.errorCollection.errors
// errmsg = "could not update priority field. check logs."
// stringbody = stringbody + ", " + errmsg.toString()
// commentManager.create(issue,user,stringbody,true)
}

1 answer

Suggest an answer

Log in or Sign up to answer
0 votes
Lasse Langhorn
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 5, 2020

Hi @djohnsoncainc

Please test this code first on your test instance. I did some minor adjustments to the code.

  • Added guard for CalcPriorityObject
  • Used constantsManager.priorities instead of constantsManager.priorityObjects (deprecated) 
// Set a priority using Post-function script on defect create
// Changes the priority of an issue depending on 2 other custom fields (severity and business impact ("bizimpact") with Script Runner
// Map this to issue creation transition - needs to be LAST post-function
// useful reference: https://community.atlassian.com/t5/Answers-Developer-Questions/How-to-change-issue-priority-with-script-runner-groovy/qaq-p/478700
// last updated: 1/25/2019 based on pilot feedback

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.comments.CommentManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.priority.Priority;
import com.atlassian.jira.issue.IssueInputParametersImpl;
import com.atlassian.jira.issue.customfields.option.Option;


// Get the current Issue
Issue issue = issue

// Get the Manager classes required
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def commentManager = ComponentAccessor.getCommentManager()
def constantsManager = ComponentAccessor.getConstantsManager()
def issueService = ComponentAccessor.getIssueService()


// Get a pointer to the current logged in user
def user = ComponentAccessor.getJiraAuthenticationContext().getUser()


// Get objects and values for relevant fields associated with the specific issue
// Found you have to explicitly cast the field values as strings for script to work

def bizimpactObject = customFieldManager.getCustomFieldObject("customfield_13402")
def severityObject = customFieldManager.getCustomFieldObject("customfield_13402")

def bizimpact = issue.getCustomFieldValue(customFieldManager.getCustomFieldObject("customfield_13402"));
bizimpact = bizimpact.toString()
def severity = issue.getCustomFieldValue(customFieldManager.getCustomFieldObject("customfield_10201"));
severity = severity.toString()

// for debugging get current issue priority values to compare
// def OrigPriority = issue.getPriorityObject().getName()
// OrigPriority = OrigPriority.toString()
// def OrigPriorityID = issue.getPriorityObject().getId()


// use priorityMatrix below as map. Has linked pair value combinations of severity, and business impact values to priority keeping code in for doc purposes only in this version
// Key at far left is severity and second dimension key is the bizimpact. These values as keys and function returns priority name (in quotes)

def priorityMatrix =
[
Blocker: [Highest: "P0 - Urgent", Significant: "P1 - High" , Average: "P1 - High", Minimal: "P2 - Medium-High"],
Major: [Highest: "P1 - High", Significant: "P2 - Medium-High", Average: "P3 - Medium-Low", Minimal: "P4 - Low"],
Minor: [Highest:"P2 - Medium-High" , Significant: "P3 - Medium-Low" , Average: "P4 - Low", Minimal: "P4 - Low"],
Trivial: [Highest:"P3 - Medium-Low" , Significant: "P3 - Medium-Low" , Average: "P4 - Low", Minimal: "P4 - Low"],
]

// use map lookup method to calculate priority and leave default value of Not Set if map is broken
def CalcPriority = "Not Set"
if (priorityMatrix.containsKey(severity) && priorityMatrix.find{bizimpact} != null) {
CalcPriority = priorityMatrix[severity][bizimpact]
} else {
log.warn "Business or Severity field selected not in matrix."
}
CalcPriority = CalcPriority.toString()
// once you have looked up the calculated priority name in the matrix, get the associated priority object based on the name

def CalcPriorityObject = constantsManager.priorities.findByName(CalcPriority)

// initialize some messages with default values
def msg1 = "No Special Message."
def errmsg = "No Errors."

// check lookup of priority by name
if (CalcPriorityObject) {
def issueInputParameters = new IssueInputParametersImpl()
issueInputParameters.setPriorityId(CalcPriorityObject.id)
issueInputParameters.setSkipScreenCheck(true)
def validationResult = issueService.validateUpdate(user, issue.id, issueInputParameters)
if (validationResult?.isValid()) {
issueService.update(user, validationResult)
} else {
log.warn("Validation errors: $validationResult?.errorCollection?.errors")
}
log.debug("Able to find priority based on lookup.")
} else {
log.warn("Not able to get priority for new defect. Leaving priority not set (default).")
}

// for debugging create a string you can write to issue comments
// def stringbody = "Current Values of Business Impact, Severity, Original Priority, Original Priority ID, New Derived Priority, New Priority ID: \n" + bizimpact + ", " + severity + ", " + OrigPriority + ", " + CalcPriority.toString() + ", " + OrigPriorityID.toString() + ", " + msg1.toString()

// this is start of code to actually set field value based on id of the Calculated priority value on the create transition only.
// first create object to hold all parameters to pass into the issue service for new issue creation

Regards

Lasse Langhorn

djohnsoncainc May 6, 2020

Thank you so very much, Lasse. Will check it out and let you know!
Donna

djohnsoncainc May 11, 2020

HI @Lasse Langhorn 
Everything works as expected on our test server after I made suggested changes. I appreciate your improvements. That said, I have not been able recently to recreate the defect condition with current code on our test environment, so will have to wait to see if these changes resolve the issue. I will reply again in another few weeks if problem is no longer observable after we promote change to production server.

djohnsoncainc July 6, 2020

Hi @Lasse_Langhorn We have now been running the changed code for several weeks and still have the same issue noted here. It is intermittent and not reproducible under normal usage conditions. Appears to be some kind of race condition. Thank you for the updates, nonetheless, as it was good for me to replace deprecated methods.

Sincerely,

Donna

TAGS
AUG Leaders

Atlassian Community Events