It’s pretty straightforward to use Sonar with your Maven projects, though as always there are a few gotchas to look out for
Fans of developer efficiency and code quality metrics will probably be familiar with the open-source Sonar application. Haters of developer efficiency and code quality metrics would have struggled to avoid it if they’ve brushed up against many “Big IT” projects in recent years.
For the uninitiated, Sonar provides a user-friendly web based view of the output from static code analysis tools such as CheckStyle, PMD and FindBugs along with unit test results and code coverage details. Each application, or code branch gets its own little summary page where you can see the state of your code at a glance and drill down into the problem areas you need to address.
It’s surprisingly easy to publish metrics from Maven projects to Sonar, though as a highly configurable, open-source tool interacting with lots of other open source tools, the secret sauce to make it work varies from deployment to deployment, and with the versions of associated tools you’re using.
Here’s how to connect a Maven 3 project to Sonar 3.3.2.
Sonar Settings
At a minimum Maven needs to be able to find your Sonar server. If you’ve installed a default Sonar instance on your local machine you might just get away with the plugin’s default settings, though if you’ve used a non-standard port number, or are communicating with a remote Sonar instance, or you have elected to use a full database server rather than its embedded one, you’ll need to point the Maven plugin in their direction.
These properties can be provided in your Maven settings.xml, a parent POM file or your project’s POM file depending on the level of re-use you’re looking for.
sonar.host.url | You’ll need to set this if your Sonar instance is running anywhere other than http://localhost:9000. |
sonar.jdbc.url sonar.jdbc.username sonar.jdbc.password sonar.jdbc.driverClassName | If you’re not using the default embedded database you’ll need to set these so Sonar can find your database. |
sonar.dynamicAnalysis | If your maven project isn’t already configured to generate test coverage reports you’ll need to set this to true to have Sonar generate them for you |
sonar.core.codeCoveragePlugin | By default Sonar uses JaCoCo for code coverage, though it also works with Emma, Clover and Cobertura. I’ve had most luck with the latter.Note that from Sonar 3.4 this property is named sonar.java.coveragePlugin. |
The following settings worked for me with a local Sonar instance on a non-default port using a local MySQL database also on a non-default port:-
<properties> <sonar.host.url>http://localhost:39000/</sonar.host.url> <sonar.dynamicAnalysis>true</sonar.dynamicAnalysis> <sonar.core.codeCoveragePlugin>cobertura</sonar.core.codeCoveragePlugin> <sonar.jdbc.url> jdbc:mysql://localhost:33306/sonar? useUnicode=true&characterEncoding=utf8 </sonar.jdbc.url> <sonar.jdbc.driverClassName> com.mysql.jdbc.Driver </sonar.jdbc.driverClassName> <sonar.jdbc.username>sonar</sonar.jdbc.username> <sonar.jdbc.password>sonar</sonar.jdbc.password> </properties>
The Maven Plugin
Now you just need to add the Sonar plugin to your Maven project:-
<build> <plugins> <plugin> <groupId>org.codehaus.sonar</groupId> <artifactId>sonar-maven3-plugin</artifactId> <version>3.3.2</version> </plugin> </plugins> </build>
First off, note that we’re using the sonar-maven3-plugin. If you’re using Maven 2.x you’ll need to use the plain sonar-maven-plugin otherwise you’ll get API incompatibility errors like the following:-
Failed to execute goal org.codehaus.sonar:sonar-maven-plugin:3.3.2: sonar (default-cli) on project MyProject: Execution default-cli of goal org.codehaus.sonar:sonar-maven-plugin:3.3.2:sonar failed: An API incompatibility was encountered while executing org.codehaus.sonar:sonar-maven-plugin:3.3.2:sonar: java.lang.NoSuchMethodError: org.apache.maven.lifecycle.LifecycleExecutor.execute (Lorg/apache/maven/execution/MavenSession; Lorg/apache/maven/execution/ReactorManager; Lorg/apache/maven/monitor/event/EventDispatcher;)V
Secondly, don’t just take the latest version of the Maven plugin – if the plugin version doesn’t match your Sonar version you can get an assortment of errors because they can’t talk to each other properly. For example, if trying to use the 3.4 plugin against a 3.3.2 Sonar:-
Failed to execute goal org.codehaus.sonar:sonar-maven-plugin:3.4: sonar (default-cli) on project MyProject: Execution default-cli of goal org.codehaus.sonar:sonar-maven-plugin:3.4:sonar failed: Fail to execute request [code=404, url=http://localhost:39000/batch_bootstrap/properties?project=MyProject]: Fail to download [http://localhost:39000/batch_bootstrap/properties?project=MyProject]. Response code: 404 -> [Help 1]
Sonar Sonar
And that’s all there is to it. Running the command mvn sonar:sonar will run your unit tests, perform a code analysis and publish the results to your Sonar server.
Many things could go wrong here of course, and I tripped over Sonar’s use of JRuby which picked up some incompatible Ruby Gems from my .rvm repository:-
Failed to execute goal org.codehaus.sonar:sonar-maven-plugin:3.3.2: sonar (default-cli) on project MyProject: Execution default-cli of goal org.codehaus.sonar:sonar-maven-plugin:3.3.2:sonar failed: Impossible to get the ID of the remote server: http://localhost:39000: Server returned HTTP response code: 500 for URL: http://localhost:39000/api/server -> [Help 1]
The 500 Error was the clue here, a quick look in the sonar.log file in the logs directory of my Sonar installation revealed the nub of the problem:
2012.12.26 16:21:43 ERROR o.s.s.ui.JRubyFacade Fail to render: http://localhost:39000/api/server undefined method `except' for #<JSON::Pure::Generator::State:0x1a7c8e4c> org/jruby/RubyKernel.java:2076:in `send' /Users/devsumo/.rvm/gems/rbx-head/gems/json-1.7.5/lib/json/pure/generator.rb:270:in `[]' ……
Since Sonar comes packaged with all its dependencies, unsetting GEM_PATH in the Sonar startup script is enough to get past this one.
Your project should now appear on your Sonar home page ready to view.