A plugin is a Set of Extensions
A SonarQube plugin is a set of Java objects that implement extension points. These extension points are interfaces or abstract classes which model an aspect of the system and define contracts of what needs to be implemented. They can be for example pages in the web application or sensors generating measures.
The extensions implemented in the plugin must be declared in a Java class extending org.sonar.api.Plugin. This class must then be declared in the pom with the property <pluginClass>:
The sonar-plugin packaging accepts advanced parameters.
An extension point is a point in the application where plugin code can be invoked, such as webapp page or code analyzer. Extension points are generally interfaces that can be implemented by plugins. Implementations have to be declared in the method org.sonar.api.SonarPlugin#getExtensions() and are then injected in the IoC container.
The extension points are listed and documented in the Javadoc. See http://docs.sonarsource.org/latest/apidocs/index.html?org/sonar/api/Extension.html.
Sensor or Decorator?
There are two extension points that allow plugins to save measures: Sensor and Decorator. A common problem when writing a plugin is to decide which one to use.
A Sensor is invoked once during the analysis of a project. The sensor can invoke a maven plugin, parse a flat file, connect to a web server... For example the Cobertura Sensor invokes the Codehaus Cobertura MOJO. Then the generated XML file is parsed and used to save the first-level of measures on resources (project, package or class).
A sensor can access and save measures on the whole tree of resources. Sensor are generally used to add measure at the lowest level of the resource tree.
Decorators are triggered once all sensors have completed. Their decorate method is called on every resource of a certain level bottom up. Decorators can load (SELECT) and save (INSERT) measures. The call is contextual, i.e it is only possible to access the resource and its children.
Decorators are generally used to consolidate at higher levels measures that have been added by Sensors at the lowest level.
How to Reuse Existing Components?
Extensions are registered in an IoC container, with constructor injection. To communicate with other extensions or with existing components provided by the API, just declare them in the constructor of your extension. For example to get references on DatabaseSession or RulesProfile:
References will be automatically set at runtime.
How to Quickly Start the Plugin?
As described in the Getting Started page, you can build sources, copy the JAR file to the directory extensions/plugins/ and restart the server. But this approach can quickly become tedious. The following solutions help to edit code without leaving your development environment:
Changes on Java code
The sonar-dev-maven-plugin lets you start a SonarQube server and deploy your plugin.
Once the server is launched, hit http://localhost:9000. By default, the in-process database (H2, or Derby prior to SonarQube 3.2) is used but you can specify a local MySQL instance instead with the property "-Dsonar.database=mysql". In that case, the sonar schema must exist in the MySQL DB along with the user sonar/sonar (login/password) which must have all rights on the sonar schema.
Then you can feed the database by inspecting projects:
Debug Java batch
mvnDebug sonar:sonar and attach your IDE to the remote process. Example in Intellij Idea: Run > Edit configurations > Add new configuration > Remote.
Debug a plugin
- Unzip a sonar distribution
- Edit the conf/wrapper.conf file and uncomment the line : wrapper.java.additional.3=-agentlib:jdwp=transport=dt_socket,server=y,address=8000
- Copy your plugin's jar file to extensions/plugins
- Launch SonarQube with the standard command. The following line will appear in the log : Listening for transport dt_socket at address: 8000
- Attach the IDE to the debug process on port 8000
- Set breakpoints in the source code
How to Use External Libraries?
A plugin benefits from all the dependencies provided by the API. Execute the following command on your plugin to list them:
Since version 2.2, the plugin classloader is isolated from other plugins, so it can embedd its own dependencies. Just define the dependencies you need with the scope 'compile'.
- Libraries used for GWT compilation must be defined with scope provided
- The plugin classloader is a child of the Sonar classloader, with a parent-first delegation model. There are two consequences:
- Sonar libraries are automatically inherited. Their versions can not be changed.
- There are side-effects on some libraries, for example the classloader must be explicitly set for XStream:
How to Log?
SLF4J is used as a simple facade of various logging frameworks (log4j, commons-log, logback, java.util.logging). It's simple to use:
Read the SLF4J manual for more details.
How to Get Configuration?
org.sonar.api.config.Settings provides properties for batch extensions (global/project settings, command-line parameters, system properties) and server extensions (global settings, system properties, file $SONAR_HOME/conf/sonar.properties). It replaces Apache Commons Configuration that is deprecated since release 2.12.
To benefit from advanced features (default value, availability in the settings page), properties should be declared on the Plugin entry point or on extensions:
Persistent properties are also accessible from the Web Service named 'properties'. To exclude some properties from anonymous requests, add the suffix ".secured" to the key (
my.property.secured). It can be useful for license keys for example.