class JiraSCMListener extends SCMListener {

    @Override
    public void onChangeLogParsed(Run<?, ?> run, SCM scm, TaskListener listener, ChangeLogSet<?> changelog) throws Exception {

        try {
            JiraSite jiraSite = JiraSite.get(run.getParent());
            if (jiraSite == null) {
                return;
            }
            Collection<String> issueKeys = getIssueKeys(changelog, jiraSite.getIssuePattern());

            if (issueKeys.isEmpty()) {
                return;
            }
            String jql = constructJQLQuery(issueKeys);
            JiraSession session = jiraSite.getSession();
            if (session == null) {
                return;
            }
            // Query for JIRA issues
            List<Issue> issues = session.getIssuesFromJqlSearch(jql);
            Set<JiraIssue> issuesFromJqlSearch = issues == null ? Collections.emptySet() :
                issues.stream().map( input -> new JiraIssue(input) )
                    .collect( Collectors.toSet() );

            // If there are no JIRA issues, do not update the actions
            if (issuesFromJqlSearch.isEmpty()) {
                return;
            }
            // Create or update the JiraBuildAction
            JiraBuildAction action = run.getAction(JiraBuildAction.class);
            if (action == null) {
                run.addAction(new JiraBuildAction(run, issuesFromJqlSearch));
            } else {
                action.addIssues(issuesFromJqlSearch);
            }
            run.save();
        } catch (Exception e ){ // we do not want to fail the build if an issue happen here
            LOGGER.warn( "Failure executing Jira query to fetch issues. Skipping recording Jira issues.: {}", e.getMessage() );
            // stack trace in debug mode
            LOGGER.debug( e.getMessage(), e);
        }
    }

}