sonarqubeThe last post was all about creating a new plugin for sonar. Now, lets get off the beaten path and create a post job in a plugin instead of a decorator or sensor. But, first it is important to understand the difference.

Please read the previous articles on sonar if your haven’t gone through them yet.

The terms Sensor and Decorator have been explained here. Please go through this article, if you are unaware of the terminology.

Where a decorator cannot be used?

Decorator execution is a post analysis step in the sonar analysis lifecycle. Allow me to call it a lifecycle here, even if it is not. But, the catch is that the analysis results are not persisted to the database yet. So, executing update commands using the web service or modifying issues in database directly is not an option as the results have not been persisted to the database yet.

PostJob to the rescue

As per the interface Phase placed in org.sonar.api.batch package, there are three phases PRE, DEFAULT and POST. A post job with @Phase(name=Phase.Name.POST) annotation is executed when the business logic is to be executed after the analysis results have been persisted to the database. If we wish to update the issues, say assign issues to some user, we can now make api calls or make changes to the database directly from the plugin itself.

Using PostJob in Plugins?

Create a post job simply by implementing org.sonar.api.batch.PostJob interface. As discussed above @Phase(name=Phase.Name.POST) will ensure execution of the post job once the analysis results have been persisted to the database.

Here is an example of a PostJob. The class implementing a PostJob must be listed in the SonarPlugin#getExtensions().

1. PluginClass

public class PluginClass extends SonarPlugin {
	public List getExtensions() {
		return ImmutableList.of(StatsCollectorPostJob.class);
	}
}

2. PostJob implementation

@Phase(name = Phase.Name.POST)
public class StatsCollectorPostJob implements PostJob {
	private static final Logger LOG = LoggerFactory
			.getLogger(ExampleSensor.class);
	ProjectIssues projectIssues;

	public StatsCollectorPostJob(ProjectIssues projectIssues) {
		this.projectIssues = projectIssues;
	}

	public void executeOn(Project project, SensorContext context) {
		LOG.info("Running post job for the project - " + project.getName());
		Iterable<Issue> issues = null;
		Iterator<Issue> itr = null;
		Issue issue = null;
		if (null != projectIssues.issues()) {
			issues = projectIssues.issues();
			itr = issues.iterator();
			while (itr.hasNext()) {
				issue = itr.next();
				LOG.info(new StringBuilder("Issue key - ").append(issue.key())
						.append(", severity - ").append(issue.severity())
						.append(", description - ").append(issue.message())
						.toString());
			}
		}
	}
}

Possible Applications

  1. Generate reports based on measures
  2. Assign issues to users
  3. Mark a build failure based to measures and metric statistics