Cross-referencing plugins in Sonar 2.2

9:37 AM , , 0 Comments

Formerly, we had three Sonar plugins, two "mavenly" dependent on the other. The parent plugin held the common code for uploading non-Java files into Sonar for reporting. The other two took care of analyzing xml and css, respectively, and tying violations to those files.

This worked great in Sonar 2.0.1, but when we upgraded to Sonar 2.2, the violation coloring stopped working on the server for these files.

What could be the problem? I walked carefully through the Sonar code and saw that the violations were making it into the database, but that two resources were getting created for each file in the project.

This didn't make a whole lot of sense since the files and their violations (and other metrics) are housed in the singleton DefaultSonarIndex as a map of Files to metrics (the actual class name for all the class metrics is called Bucket). What could be causing to records of the same file to make it into the map.

Enter the Resource equals method:


1. public boolean equals(Object o) {
2. if (this == o) {
3. return true;
4. }
5. if (o == null || getClass() != o.getClass()) {
6. return false;
7. }
8.
9. Resource resource = (Resource) o;
10. return key.equals(resource.key);
11.
12. }

This is a pretty standard looking equals method that doesn't really seem to be the suspect since I know that they keys are the same by verifying it in my debugger. The crazy thing is that it breaks on line 5.

What?? o is definitely not null and they are definitely the same class...oh, right...as of Sonar 2.2, each plug-in loads in its own classloader. The first loading of the class was by the parent plugin to load the source into Sonar and the second loading was by the child plugin to specify violations.

So the quick solution was to take the small amount of code in the parent project and distribute it to the others. The cleaner fix would be to refactor it so that only one project is actually referring to the classes (probably the children).

Phew. That was a tricky one.

0 comments:

Integration Tests in Sonar

Integration tests are another important aspect of analyzing a project's overall health that Sonar does not yet support out of the box. To get this functionality, you'll need to build a couple of Sonar plugins (or try using the ones that I built) that will instrument your integration test code, run the integration tests, and collect the integration test results as well as the new coverage data.

It sounds like a tall order, but a lot of the work has already been done for you.

Sonar runs the unit tests in your project automatically by way of surefire. Sonar has a surefire plugin which executes the surefire:test goal and then collects the results by reading the TEST-xxx.XML files that it produces.

So, why not do the same with failsafe, the maven plugin for running integration tests? Sounds pretty simple and there are only a couple of catches.

The first catch is that there are not pre-defined metrics in Sonar for integration tests. So, if you don't mind piggybacking on the unit test metrics--meaning that your success, failure, and coverage numbers will be aggregated across both unit and integration--then simply copy the SurefireSensor, changing only the part where you are saving the metrics to updating existing metrics.

The second is a corner case. What if you don't have any unit tests, but you do have integration tests? If the surefire sensor doesn't find any unit tests to run, then it preemptively sets one of the metrics that later on is overwritten by the UnitTestDecorator (read: things explode). The only way around this one (that I've found) is to add another sensor to our failsafe plugin that, when the Sonar batch process first starts up, creates a dummy surefire empty test result for the surefire sensor to catch. While temporary and dummy files give me an icky feeling inside, it does the trick.

One more involved way around both of these is to create your own set of metrics and a decorator to display them. In this way, you wouldn't collide with any of Sonar's efforts to run unit tests. I haven't done this, yet, and I think that there might be more involved than that, so I didn't touch that route.

Okay, the failsafe plugin takes care of running and reporting on integration test execution. What about code coverage?

My coverage tool of choice is Emma. A while ago, Sonatype wrote an article explaining how to use emma4it to add integration test code coverage to emma. So, we can follow the same pattern, creating a Sonar plugin to execute the appropriate maven commands.

The only tricky part here was telling Sonar when to run each command. As far as I understand it, one cannot specify maven lifecycle points at which to run each maven goal. Instead, Sonar invokes all the goals serially at the point when it's that sensor's turn to execute.

What we really want is emma:instrument and emma4it:instrument-project-artifact to happen together, then the failsafe sensor, and then emma4it:report. Hmm...

The way we solved it was to have three different MavenPluginHandlers and Sensors in our emma4it sonar plugin. The emma:instrument and emma4it:instrument-project-artifact are dependent on emma finishing and emma4it:report is configured as running in the Phase.Name.POST phase. Failsafe, also specified as being dependent on code coverage finishing falls by default in between the two.

If there were a closer mapping to post-integration-test, process-classes, etc. in the future, this piece would become a lot cleaner.

I'm going to spend a bit of time cleaning up my code before I upload it, but after that, you are free to code by example. :)
Enhanced by Zemanta

1 comments:

Reporting more than Java code in Sonar (Part I)

8:57 AM , , 2 Comments

Of course, anyone that has done static analysis on their project in the past has found certain bad practices that are out of their tools reach to spot. Some examples are:
  • Front-end code, like CSS and HTML
  • Configuration files, like a Maven pom.xml or a Spring applicationContext.xml
  • Localization files
While not supported out-of-the-box, Sonar makes reaching and reporting on these areas of your project much easier. Basically, here are the steps to getting Sonar to report on additional languages:
  1. Make Sonar aware of your new language.
  2. Attach quality rules for that language to an existing Quality Profile.
  3. Create/use a tool that will detect the bad practices you are looking for.
  4. Hook that tool together with Sonar either via the tool's Maven build plug-in or by invoking it programmatically within the Sonar plug-in framework.

Make Sonar Aware of Your New Language

First, it is easy to make Sonar aware of an additional language. In our case, we wanted to address configuration found in various xml application files for Spring, Maven, JSF, and the like. The following seven files are required:
  • XmlPlugin.java - What you are making in the most basic sense is a Sonar plug-in. For each sonar plug-in, there is a main plugin file like this one. I won't go into this here. Instead, I'd recommend you look at the Sonar Plug-in Documentation.
  • Xml.java - This file is what the rest of Sonar will refer to when it asks what language a file is, etc. Ours looks like this:

    public class Xml extends AbstractLanguage {
    protected static final String[] EXTENSIONS = { "xml", "xhtml" };
    public static final Xml INSTANCE = new Xml();

    public Xml() {
    super("xml", "XML"); // 'key' and 'name', or, in other words, internal and external names
    }

    public String[] getFileSuffixes() {
    return EXTENSIONS.clone();
    }

    // ... some other helper methods
    }


  • XmlFile.java and XmlPackage.java - These two classes represent the xml file and directory metadata. They aren't a way to get at the contents of the file, but instead its name, location, etc. The both extend org.sonar.api.resources.Resource.

  • XmlSourceImporter.java - This file is in charge of looking up all the "xml" files in the project and notifying Sonar about them. Because we also want to include the main pom.xml file, which is outside of the source directory, this class is a little more complicated. However, you can easily pull out the basics from it:

    public class XmlSourceImporter extends AbstractSourceImporter {
    public XmlSourceImporter() {
    super(Xml.INSTANCE);
    }

    public void analyse(Project project, SensorContext context) {
    try {
    doAnalyse(project, context);
    } catch ( IOException e ) {
    throw new SonarException("Parsing source files ended poorly", e);
    }
    }

    protected XmlFile createResource(File file, List sourceDirs, boolean unitTest) {
    ... create an XmlFile object ...
    }

    /* Depending on needs, one might ask the kind of project that it is or something. In this case, though, we want to execute this importer on every project, since we are anticipating the existence of xml files in every project. */
    public boolean shouldExecuteOnProject(Project project) {
    return isEnabled(project);
    }

    protected void doAnalyse(Project project, SensorContext context) throws IOException {
    ProjectFileSystem fileSystem = project.getFileSystem();
    File root = fileSystem.getBasedir();
    List sourceDirs = fileSystem.getSourceDirs();
    sourceDirs.add(root);
    List xmlFiles = ...magical method call that looks recursively in the sourceDirs for xml files...
    for ( File xmlFile : xmlFiles ) {
    // if it is in a derived directory, like the build directory, we don't want it
    if ( DefaultProjectFileSystem.getRelativePath(xmlFile, fileSystem.getBuildDir()) == null ) {
    sourceFiles.add(xmlFile);
    }
    }

    }
    }


Attach Quality Rules

Now, what is not so obvious is how to get Sonar to report on all languages, Java and otherwise, on one dashboard. In fact, in an email that I sent to the the Sonar developers, they apparently don't officially support it, yet. But, we found a way! And it works great for us.

The key lies in creating a rules repository that contributes to the existing Java quality profile that you already have set up. A rules repository is just another Sonar extension, one that represents an xml rule configuration file and marshals the contents of that file into a RulesProfile object.

Here is an example of what the getProvidedProfiles might look like:


public List getProvidedProfiles() {
RulesProfile profile = new RulesProfile("My Profile", Java.KEY);
profile.setDefaultProfile(true);
profile.setProvided(true);

List rules = getInitialReferential();
List activeRules = new ArrayList();
for (Rule rule : rules) {
activeRules.add(new ActiveRule(profile, rule, rule.getPriority()));
}
profile.setActiveRules(activeRules);

return Arrays.asList(profile);
}


In the end, it's a little bit of a hack.

The only other thing that is necessary from Sonar's perspective is a way to read the violations file that your analysis tool creates. We modeled ours after PMD's violations file. Here, you can extend AbstractViolationsXmlParser and follow the pattern in the PMD Sonar Plugin.


Enhanced by Zemanta

2 comments: