Tuesday, September 10, 2013

Suppressing PMD alerts for Lombok generated code

As you know from my previous post, Lombok offers Java programmers a convenient equivalent to Scala's Case Classes. And the lombok-maven-plugin allows for easy integration of Lombok into a Maven project.

I am a big fan of Static Code Analysis tools like PMD and FindBugs. Generally, one could argue that it is not necessary to run checks against generated code because it is not the source code that you are maintaining. For code generation tools like ANTLR, I think skipping checks is prudent because if the generated code does not comply with your quality standards, there may be little that one can do — the source code that should be scrutinized is the ANTLR grammar.

When using Lombok, the tool may generate some boiler-plate code for you, like getters, setters, equals, and hashCode, but you often write some additional methods that you would want to have PMD analyze. As a result, excluding PMD from processing your classes is not recommended. And then you get second thoughts after seeing the consequences of running PMD against a generated equals and hashCode implementation, complaining about missing braces, multiple exit points, complexity, etc. Worse is that all that crying wolf makes it hard to focus on the real problems that you need to address.

You may notice, however, that Lombok always adds an annotation to generated code:

@java.lang.SuppressWarnings("all")
This annotation is supposed to hint to tools that this code can be ignored because it is generated.

Prior to PMD 5, you could suppress PMD for a class or a line, but not for a method. PMD 5 offers two new suppression techniques, one of which we can leverage for suppressing checks against Lombok code: Violation Suppress XPath.

Certain rules have a tendency to fire false alarms for Lombok generated code:

For these rules, we can add an XPath expression for the Suppress Warnings annotation so that those rules will be skipped for Lombok generated methods.

The XPath expression for the Suppress Warnings annotation is:

//MethodDeclaration[../Annotation/SingleMemberAnnotation[ (Name/@Image='java.lang.SuppressWarnings') and (MemberValue/PrimaryExpression/PrimaryPrefix/Literal/@Image='"all"')]]

The rule can be configured with the Violation Suppress XPath like this:

<rule
  ref="rulesets/java/braces.xml/IfStmtsMustUseBraces">
  <properties>
    <property name="violationSuppressXPath"
      value="//MethodDeclaration[../Annotation/SingleMemberAnnotation[
(Name/@Image='java.lang.SuppressWarnings') and (MemberValue/PrimaryExpression/PrimaryPrefix/Literal/@Image='"all"')]]" />
  </properties>
</rule>

The Sample Maven Lombok Project has been enhanced to demonstrate this.

Monday, September 2, 2013

Mavenizing the Flex SDK - The Missing Documentation

For those of us using Maven and Flex, we need to get the Flex artifacts uploaded to a Maven repository. In my case, I have an Artifactory instance that can house corporate artifacts and third-party artifacts, and proxy artifacts available on the internet.

The Apache Flex project has a couple of SDK utilities that greatly help moving all the necessary artifacts into a Maven repository. Specifically:

  1. Apache Flex SDK Installer
  2. Apache Flex SDK Mavenizer

The Apache Flex SDK Installer is a download utility that will simplify pulling down Adobe AIR, Flex SDK, and Flash Player. After installing the installer, run it to select the versions that you want to download. For my case, I downloaded:

  • Flex SDK 4.10.0
  • AIR 3.8
  • Flash Player 11.8
I specified an empty installation directory that I created first (C:\temp\apache\flexsdk), checked all the the license agreements, and then clicked on Install.

The Apache Flex SDK Mavenizer seems to only be available in source-code form. It can be downloaded using either Subversion or Git:

Subversion
https://svn.apache.org/repos/asf/flex/utilities/trunk/mavenizer/
Git
https://git-wip-us.apache.org/repos/asf/flex-utilities.git

Checkout (Subversion) or Clone (Git) the project, then build it using maven. For example:

C:\temp\apache\mavenizer> mvn package

The Apache Flex SDK Mavenizer has the following usage:

java -cp target/flex-sdk-converter-1.0.jar SDKGenerator "[sdkhome]" "[fdktarget]" false
where the sdkhome directory is structured like:
[sdkhome]
  +- air
    +- AdobeAIRSDK-[Airversion]
  +- flex
    +- flex_sdk_[Flexversion]

The Apache Flex SDK Installer downloads everything into one directory (C:\temp\apache\flexsdk), but the Apache Flex SDK Mavenizer expects the AIR SDK and the Flex SDK to be in different directories. Rather than trying to dissect the AIR SDK from the Flex SDK, the easiest solution is to duplicate the one directory so that it fits the necessary structure. For example:

C:\temp\apache> mkdir sdkhome
C:\temp\apache> mkdir sdkhome\air
C:\temp\apache> mkdir sdkhome\flex
C:\temp\apache> xcopy /s flexsdk sdkhome\air\AdobeAIRSDK-3.8\
C:\temp\apache> xcopy /s flexsdk sdkhome\flex\flex_sdk_4.10.0\

Next, create an empty target directory that will contain the artifacts in Maven format:

C:\temp\apache> mkdir fdktarget

Now you can run the SDK Generator step of the Apache Flex SDK Mavenizer:

C:\temp\apache\mavenizer> java -cp target\flex-sdk-converter-1.0.jar SDKGenerator "C:\temp\apache\sdkhome" "C:\temp\apache\fdktarget\" false
Note that the last argument is false as long as flexmojos-maven-plugin will be used; when maven-flex-plugin is used, then true will be specified.

Feel free to inspect the results of fdktarget. There are a lot of files -- too many to deploy manually to Artifactory. Luckily, the Apache Flex SDK Mavenizer utility has the SDK Deployer step that will upload these files to a repository. For example:

C:\temp\apache\mavenizer> java -cp target\flex-sdk-converter-1.0.jar SDKDeployer "C:\temp\apache\fdktarget\" flex-releases http://ArtifactoryHost/artifactory/third-party-releases-local/ "C:\apache-maven-3.0.4\bin\mvn.bat"

Note that flex-releases refers to a <server> entry in my Maven settings.xml that contains credentials for Artifactory. For example:

<settings>
  <servers>
    <server>
      <id>flex-releases</id>
      <username>awhitford</username>
      <password>super-secret</password>
    </server>
  </servers>
</settings>

There is a lot to upload, so be patient.

Monday, September 5, 2011

lombok-maven-plugin 0.10.0.2 is available

The clever team behind Project Lombok released a new version recently: 0.10.0. This new version includes some breaking changes to the internals rendering my maven-lombok-plugin unable to adapt. But do not fret, because lombok-maven-plugin has been born to take its place.

The new plugin can take advantage of Lombok 0.10.0 which includes several bug fixes and new features such as:

  1. Support for Java 6 and Java 7
  2. A new val keyword (like Scala)
  3. Support for Lazy Getters
  4. Convenient annotations for a Logger
  5. A new annotation supporting easy delegation
  6. Delombok will remove lombok annotations
Also, the new plugin introduces a testDelombok goal for those who are interested in leveraging lombok for test code.

Finally, I discovered that the project was not Mac friendly, mostly due to the fact that Lombok requires tools.jar and that does not exist on a Mac (it is combined with classes.jar which is included automatically). I wanted to come up with a strategy where projects would work for Windows, Macs, and Linux, and so the Introduction shows how a Maven Profile can be added to achieve platform portability.

Monday, September 27, 2010

An unpleasant surprise from Scala 2.8

Generally speaking, I am a big fan of the "latest and greatest" when it comes to software versions. For example, when I have a dependency on library 2.3 and I notice library 2.4 is out, I generally update my dependency. I think this is a good strategy so that your software stays current, is able to leverage the new features, and, let's face it, run the latest set of bugs.

Well, Scala 2.8.0 was released in July. I know a lot of work went into the release. Naturally, I took my Scala 2.7.7 code and upgraded it to Scala 2.8.0.

After updating dependencies, the first hurdle was to make sure the code compiled. I found the new Scala 2.8 compiler to be more strict -- which is perfectly fine. A few things had been deprecated, but it was great how the compiler told me what to do. For example, instead of using first, use head instead. This makes sense because the Collections API had a serious overhaul with 2.8, rationalizing methods and making functions consistent across the variety of different kinds of collections.

After the code compiled and the unit tests passed, I did a smoke test and felt pretty pleased with the results. I had not made any significant code change -- just made it 2.8 compliant. I naively thought I was done... But of course I was not...

A thorough regression test was required! Some very subtle differences were discovered that interfered with algorithms. For example:
  1. BufferedSource.getLines
    With Scala 2.7.7, the return value will include the end of line character(s), but Scala 2.8.x does not!
  2. XML processing behaves differently. Consider the following code:

    import scala.xml._
    
    val path : NodeSeq = readme.txt
    val kind = path \ "@kind"
    val isFile = kind equals "file"
    

    The above code compiles in both Scala 2.7.7 and Scala 2.8.x. The path and kind variables are evaluated exactly the same, but the isFile result is different: Scala 2.7.7 returns true, while Scala 2.8.x returns false. Wow, that is enough to break an algorithm!

    FWIW, replacing the last line with the following will work in either 2.7.x or 2.8.x:

    val isFile = (kind mkString) equals "file"
    

It made me also think about how Scala's implicit conversions could be introduced with a new version that could unknowingly change an algorithm.

Let me add that changing the behavior of getLines to exclude the end of line character(s) was likely a smart change. If the documentation in Scala 2.7.7 did not explicitly say that it was including the end of line character(s), I probably would have assumed that it did not. So now the implementation matches what one would expect. Of course, I am a little surprised that in this case they were willing to change the definition of the function and its documentation rather than deprecate it and suggest an alternative function with slightly different semantics.

And while I have not fully traced the XML change, I am sure that there was method to the madness. Perhaps there was a loophole that my code was exploiting, and they closed it.

After these discoveries, I wanted to read the release notes again. What else did I miss? What else should I look out for? Alas, I do not see any mention of these changes in any release notes or even the issue tracker! (If someone knows of where this was documented, please notify me as I am clearly not looking in the right place(s).)

I will admit that some issues could have been caught sooner if there was more unit tests. We could all strive to write more unit tests.

In conclusion, be careful! I still like Scala, and this issue could have occurred with almost any dependency. You have been warned.

Thursday, September 9, 2010

Announcing the Maven Plugin for Project Lombok

My favorite programming language is Scala.  I've long argued that the number of bugs in code is directly proportional to the number of lines of code.  Scala has several features that help you avoid boring boiler-plate code, like Case Classes.

Case Classes allow you to concisely describe a data container class. For example:

case class Book(title: String, published: Date, author: String)

This is practically the equivalent to the following Java code:

public class Book {
  private String title;
  private Date published;
  private String author;

  public Book (final String title, final Date published, final String author) {
    this.title = title;
    this.published = published;
    this.author = author;
  }

  public String getTitle() {
    return this.title;
  }

  public void setTitle(final String title) {
    this.title = title;
  }

  public Date getPublished() {
    return this.published;
  }

  public void setPublished(final Date published) {
    this.published = published;
  }

  public String getAuthor() {
    return this.author;
  }

  public void setAuthor(final String author) {
    this.author = author;
  }

  @Override
  public boolean equals(final Object o) {
    if (o == this) return true;
    if (o == null) return false;
    if (o.getClass() != this.getClass()) return false;
    final Book other = (Book)o;
    if (this.getTitle() == null ? other.getTitle() != null : !this.getTitle().equals(other.getTitle())) return false;
    if (this.getPublished() == null ? other.getPublished() != null : !this.getPublished().equals(other.getPublished())) return false;
    if (this.getAuthor() == null ? other.getAuthor() != null : !this.getAuthor().equals(other.getAuthor())) return false;
    return true;
  }

  @Override
  public int hashCode() {
    final int PRIME = 31;
    int result = 1;
    result = result * PRIME + (this.getTitle() == null ? 0 : this.getTitle().hashCode());
    result = result * PRIME + (this.getPublished() == null ? 0 : this.getPublished().hashCode());
    result = result * PRIME + (this.getAuthor() == null ? 0 : this.getAuthor().hashCode());
    return result;
  }

  @Override
  public String toString() {
    return "Book(title=" + this.getTitle() + ", published=" + this.getPublished() + ", author=" + this.getAuthor() + ")";
  }
}
Wow, that is a lot of Java code replaced by one line of Scala code!  Sure, Eclipse can generate the getters and setters, but maintenance of the above code can be error prone, especially for things like equals and hashCode.  Imagine that a new field is added, and you remember to add the getter and setter, but forget to add it to the equals and hashCode methods -- you may find out the hard way (i.e. at runtime) that there is a bug.

Well, thanks to Project Lombok, Java programmers can get the benefit of Scala Case Classes in Java.  The above code can be generated based on the following Java code:

public @lombok.Data class Book {
  private String title;
  private Date published;
  private String author;
}

It is not quite as concise as Scala, but a vast improvement.  Lombok will, during compilation, generate the getters, setters, equals, hashCode, and toString for a class annotated with @Data.  (Please read more about Project Lombok's features if you are not familiar with the project.)

One of the wrinkles with using Lombok is that the real code is generated magically during compilation.  Generally, you do not quite see the resulting Java code.  This will mean things like Javadoc will look incomplete because you will not see the getters and setters, for example, yet they are logically there.  Cobertura Code Coverage reports will look incomplete or incorrect.  Static code analysis tools, like PMD, may incorrectly complain that there are private fields with no getter or setter.

The clever minds behind Project Lombok do have a solution to this problem: delombok.  They provide a tool that takes the lombok annotated Java code and expands it to the end result Java code.  By integrating this step into the build process, one can mitigate the aforementioned shortcomings.

The team initially provided a delombok Ant task, but did not yet have a Maven plugin. Rather than integrating the Ant task into a Maven project using the handy AntRun plugin, I figured that it would be worth wrapping the Ant task in a Maven Mojo.  After a couple of evenings, the maven-lombok-plugin was born.

The lombok annotated source code needs to be placed in a separate directory:  src/main/lombok (not src/main/java).  Then, during the generate-sources phase, the lombok source is delomboked and placed in target/generated-sources/delombok.  Finally, the compile phase will compile the delombok source along with the Java source.

There is a companion Sample Maven Lombok Project that demonstrates how the DataExample class with lombok annotations is transformed into detailed Java code legible by Javadoc, Cobertura, and JXR.

Wednesday, September 8, 2010

Hello World!

In true coder fashion, my premier post is a simple, "Hello World!"