AspectJ checks and Clover instrumentation

In our projects we're using Atlassian's Clover as code coverage tool. In combination with Atlassian's Bamboo and Sonar this is a very powerful setup to control your project metrics. With the newest release of Bamboo it’s now easier then ever before to include Clover in your plans and get the result back in Bamboo. So I start including this in a couple of older projects which were still using on Cobertura for their code coverage and I ran into a problem with one of those projects.

This particular project was using AspectJ to do some sensibility checks during the test phase of maven. Checks like making sure that the domain-layer isn’t calling the service-, or web-layer, but also it nobody used System.out or System.err. Of course this is a valid check, you should use a proper logging framework for this purpose.



Check.aj
pointcut systemOutOrErrAccess()
  : get(* System.out) || get(* System.err);



declare error
  : systemOutOrErrAccess()
  : “NEVER write to the console using System.out, use a logging framework!”;



Running clover
I just followed the getting started guide by Atlassian and tried running it like this:
  mvn clean clover2:instrument test clover2:aggregate clover2:clover
The result of this run was a long list of errors by our AspectJ checks stating that we should not use System.err.



So I started investigating and had a good look at an instrumented class, in this case a Fact. Clover will add a inner Class with a generated name, but always with __ in front of it. Below is an example of such an instrumented class.
...
public class Fact extends BaseEntity {
public static class __CLR2_6_39x9xg77o7m0v {
public static final com_cenqua_clover.CoverageRecorder R;
static {
com_cenqua_clover.CoverageRecorder _R = null;
try {
com_cenqua_clover.CloverVersionInfo.An_old_version_of_clover_is_on_your_compilation_classpath___Please_remove___Required_version_is___2_6_3();
if (20091120171610861L != com_cenqua_clover.CloverVersionInfo.getBuildStamp()) {
com_cenqua_clover.Clover
.l("[CLOVER] WARNING: The Clover version used in instrumentation does not match the runtime version. You need to run instrumented classes against the same version of Clover that you instrumented with.");
com_cenqua_clover.Clover.l("[CLOVER] WARNING: Instr=2.6.3#20091120171610861,Runtime=" + com_cenqua_clover.CloverVersionInfo.getReleaseNum()
+ "#" + com_cenqua_clover.CloverVersionInfo.getBuildStamp());
}
          _R = com_cenqua_clover.Clover.getNullRecorder();
          _R = com_cenqua_clover.Clover.getRecorder(new char[] { 67, 58, 92, 68, 101, 118, 101, 108, 111, 112, 109, 101, 110, 116, 92, 83, 86, 78, 82,
101, 112, 111, 115, 105, 116, 111, 114, 121, 92, 52, 50, 92, 83, 78, 76, 95, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 92,
116, 97, 114, 103, 101, 116, 47, 99, 108, 111, 118, 101, 114, 47, 99, 108, 111, 118, 101, 114, 46, 100, 98 }, null, 1269528030602L,
                8589935092L, 432);
        } catch (java.lang.SecurityException e) {
java.lang.System.err
.println("[CLOVER] FATAL ERROR: Clover could not be initialised because it has insufficient security privileges. Please consult the Clover documentation on the security policy file changes required. ("
+ e.getClass() + ":" + e.getMessage() + ")");
} catch (java.lang.NoClassDefFoundError e) {
java.lang.System.err.println("[CLOVER] FATAL ERROR: Clover could not be initialised. Are you sure you have Clover in the runtime classpath? ("
+ e.getClass() + ":" + e.getMessage() + ")");
} catch (java.lang.Throwable t) {
java.lang.System.err.println("[CLOVER] FATAL ERROR: Clover could not be initialised because of an unexpected error. (" + t.getClass() + ":"
+ t.getMessage() + ")");
}
        R = _R;
      }
  }



  public static final int NOTE_LENGHT = 2048;
....



Because Clover never knows in what kind of environment it’s being used it obviously makes use off the, always present, System.err. Since we made the decision that we want to throw an error if someone uses System.err this is picked up by our aspect and indeed an error is thrown. I wanted to fix this in our aspect so that we could use Clover for the code coverage.



The solution
I added another pointcut and added this to the error definition



pointcut isCloverInstrumented()
  : within(*..__*);
declare error
  : systemOutOrErrAccess() && !isCloverInstrumented()
  : “NEVER write to the console using System.out, use a logging framework!”;



Which basically checks if a class starts with “__” so potential excluding more class in your application.



This works like a charm, now we are still able to check the integrity of our application without having to abandon Clover as Code Coverage tool. Bamboo and Sonar both can use Clover now .