Tutorial: Correct SLF4J logging usage and how to check it

SLF4J is a very popular logging facade but, like all libraries we use, there is a chance that we use it in a wrong or at least in a not optimal way.

In this tutorial we will list common logging errors and how we can detect them using FindBugs. We will also mention PMD and Sonar Squid checks when relevant.

We will use two external FindBugs plugins which add logging detectors to FindBugs.

The first one is a SLF4J only plugin by Kengo Toda which contains SLF4J detectors only.

The second plugin is the popular FB Contrib which contains, among many others, some logging detectors.

For how to use FindBugs plugins, please refer to the following posts:

Note: in all examples we will assume the following imports:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

1. Logger definition

Wrong way:

W1a. Logger log = LoggerFactory.getLogger(MyClass.class);
W1b. private Logger logger = LoggerFactory.getLogger(MyClass.class);
W1c. static Logger LOGGER = LoggerFactory.getLogger(AnotherClass.class);

Correct way:

C1a. private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
C1b. private final Logger logger = LoggerFactory.getLogger(getClass());

General rule: the logger should be final and private because there are no reasons to share it with other classes or to re-assign it.

On the contrary there is no general agreement if the logger should be static or not. SLF4J plugin favors non static version (C1b) while PMD (“LoggerIsNotStaticFinal” rule) and Sonar (squid rule S1312) prefer a static logger (C1a) so both options should be considered as valid.

Additional info:

Please note that

  • in the static version (C1a), the logger name is usually in uppercase characters as all constant fields. If not, PMD will report a “VariableNamingConventions” violation.
  • in both cases, the suggested name is “logger/LOGGER” and not “log/LOG” because some naming conventions avoid too short names (less than four characters). Moreover log is the verb, more suited for a method name.
  • the W1c is wrong because we are referring to a class (AnotherClass) which is not the class where the logger is defined. In the 99% of the cases, this is due to a copy & paste from one class to another.

Related FindBugs (SLF4J plugin) checks:

  • SLF4J_LOGGER_SHOULD_BE_PRIVATE
  • SLF4J_LOGGER_SHOULD_BE_NON_STATIC
  • SLF4J_LOGGER_SHOULD_BE_FINAL
  • SLF4J_ILLEGAL_PASSED_CLASS

 

2. Format string

Wrong way:

W2a. LOGGER.info("Obj=" + myObj);
W2b. LOGGER.info(String.format(“Obj=%s”, myObj));

Correct way:

C2. LOGGER.info("Obj={}",myObj);

General rule: the format string (the first argument) should be constant, without any string concatenation. Dynamic contents (the myObj value in the example) should be added using the placeholders (the ‘{}’ ).

Motivation is simple: we should delay logging message creation after the logger has established if the message should be logged or not, depending on the current logging level. If we use string concatenation, message is built any way, regardless the logging level which is a waste of CPU and memory resources.

Related FindBugs (SLF4J plugin) checks:

  • SLF4J_FORMAT_SHOULD_BE_CONST Format should be constant
  • SLF4J_SIGN_ONLY_FORMAT Format string should not contain placeholders only

Related FindBugs (FB Contrib plugin) checks:

  • LO_APPENDED_STRING_IN_FORMAT_STRING Method passes a concatenated string to SLF4J’s format string

 

3. Placeholder arguments

Wrong way:

W3a. LOGGER.info("Obj={}",myObj.getSomeBigField());
W3b. LOGGER.info("Obj={}",myObj.toString());
W3c. LOGGER.info("Obj={}",myObj, anotherObj);
W3d. LOGGER.info("Obj={} another={}",myObj);

Correct way:

C3a. LOGGER.info("Obj={}",myObj);
C3b. LOGGER.info("Obj={}",myObj.log());

General rule: the placeholder should be an object (C3a), not a method return value (W3a) in order to post-pone its evaluation after logging level analysis (see previous paragraph). In W3a example, the mehod getSomeBigField() will be always called, regardless the logging level. For the same reason, we should avoid W3b which is semantically equivalent to C3a but it always incurs in the toString() method invocation.

Solutions W3c and W3d are wrong because the number of placeholders in the format string does not match the number of placeholders arguments.

Solution C3b could be somehow misleading because it includes a method invocation but it could be useful whenever the myObj contains several fields (for example it is a big JPA entity) but we do not want to log all its contents.

For example, let’s consider the following class:

public class Person {
private String id;
private String name;
private String fullName;
private Date birthDate;
private Object address;
private Map<String, String> attributes;
private List phoneNumbers;

its toString() method will most probably include all fields. Using the solution C3a, all their values will be printed in the log file.

If you do not need all this data, it is useful to define a helper method like the following:

public String log() {
return String.format("Person: id=%s name=%s", this.id, this.name);
}

which prints relevant information only. This solution is also CPU and memory lighter than toString().

What is relevant ? It depends on the application and on the object type. For a JPA entity, I usually include in the log() method the ID field (in order to let me find the record in the DB if I need all columns data) and, may be, one or two important fields.

For no reason, passwords fields and/or sensitive info (phone numbers,…) should be logged. This is an additional reason to not log using toString().

Related FindBugs (SLF4J plugin) checks:

  • SLF4J_PLACE_HOLDER_MISMATCH

 

4. Debug messages

IMPORTANT: rule #4 (see 5 rules article) guide us to use a guarded debug logging

if (LOGGER.isDebugEnabled()) {
LOGGER.debug(“Obj={}”, myObj);
}

Using SLF4J, if the placeholder argument is an object reference (see solutions C3a/C3b), we can use avoid the if in order to keep the code cleaner.

So it is safe to use the following:

LOGGER.debug(“Obj={}”, myObj);

 

5. Exceptions

Proper exceptions logging is an important support for problems analysis but it is easy to neglect its usefulness.

Wrong way:

W5a. catch (SomeException ex) { LOGGER.error(ex);}..
W5b. catch (SomeException ex) { LOGGER.error("Error:" + ex.getMessage());}..

Correct way:

C5. catch (SomeException ex) { LOGGER.error("Read operation failed: id={}", idRecord, ex);}..`

General rules:

  1. Do not remove the stack trace information by using getMessage() (see W5b) and not the complete exception. The stack trace often includes the real cause of the problem which is easily another exception raised by the underlying code. Logging only the message will prevent us to discover the real cause of the problem.
  2. Do show significant (for the human which will analyze the log file) information in the logging message showing a text explaining what we wanted to perform while the exception was raised (not the exception kind or messages like “error”: we know already something bad happened). What we need to know is what we were doing and on which data.

The C5 example tells we were trying to read the record with a specific ID whose value has been written in the log with the message.

Please note that C5 use one placeholder in the format string but there are two additional arguments. This is not an error but a special pattern which is recognized by SLF4J as an exception logging case: the last argument (ex in the C5 example) is considered by SLF4J as a Throwable (exception) so it should be not included in the format string.

Related FindBugs (SLF4J plugin) checks:

  • SLF4J_MANUALLY_PROVIDED_MESSAGE: the message should not be based on Exception getMessage()
Advertisements

Tutorial: using FindBugs with Maven

FindBugs can be executed using Maven in two different modes: as stand-alone command or as part of the Maven site command.

These two modes require different settings in the project pom.xml but they are not incompatible to each other so we can use both of them.

info48
You can find complete Maven project here.

Stand-alone mode

Let’s start with the stand-alone mode which requires the following configuration in the pom.xml:

<build>
    <plugins>       
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>findbugs-maven-plugin</artifactId>
            <version>3.0.1</version>
        </plugin>
         ... other build plugins ....
    </plugins>
</build>

Please note that the FindBugs plugin definition is inside build – plugins section of the pom file.

Giving the following command:

mvn findbugs:findbugs

FindBugs is run against our project with the following log in the console output:

[INFO] --- findbugs-maven-plugin:3.0.1:findbugs (default-cli) @ CodeQualityGallery ---
[INFO] Fork Value is true
     [java] Warnings generated: 1
[INFO] Done FindBugs Analysis....

warning48
FindBugs analyze the compiled files (*.class). If the project is not compiled or just cleaned, FindBugs will report nothing, without error messages. So take care to build your project before running FindBugs.

A warnings number is reported on the console. Warnings are possible bugs found by the FindBugs detectors.

The warnings details are included in a report created in the file target/findbugsXml.xml which is in XML format and so not really readable.

A better way to examine the warnings is to use the FindBugs native GUI with the command:

mvn findbugs:gui

The FindBugs window shows the bugs on the left, the (read-only) code on the right and the bug explanation below.

FindBugs GUI

You can filter bugs for bug rank from the most critical (“scariest”) to trivial and even, saving project status, comparing project evolution (which means how may bugs has been eliminated/introduced after previous FindBugs check).

warning48All main IDEs (Eclipse, NetBeans, IntelliJ) FindBugs plugins show similar views and they can also let you modify the code so I don’t suggest to use FindBugs native GUI unless you have to create exclusion files (see below) which is easier with the native GUI: select a bug, filter it out and export bug filters.

 

Site mode

Maven site command is used to generate a project “site”, which is a collection of information on the project nicely reported in HTML pages. You can configure which information is included in the site report and, of course, you can add FindBugs analysis results.

Maven site requires a “reporting section” in the pom file.

<reporting>
    <plugins>       
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>findbugs-maven-plugin</artifactId>
            <version>3.0.1</version>
        </plugin>
         ... other site plugins ....
    </plugins>
</reporting>

Giving the following command (note: clean and install options are in to assure that project is compiled and all tests run):

mvn clean install site

on the console output we will see the following:

.....
[INFO] --- maven-site-plugin:3.3:site (default-site) @ CodeQualityGallery ---
[INFO] configuring report plugin org.apache.maven.plugins:maven-jxr-plugin:2.5
[INFO] configuring report plugin org.apache.maven.plugins:maven-project-info-reports-plugin:2.8
[INFO] configuring report plugin org.codehaus.mojo:findbugs-maven-plugin:3.0.1
[INFO] Fork Value is true
     [java] Warnings generated: 2
[INFO] Done FindBugs Analysis....
[INFO] configuring report plugin org.apache.maven.plugins:maven-pmd-plugin:3.4
[WARNING] No project URL defined - decoration links will not be relativized!
[INFO] Rendering site with org.apache.maven.skins:maven-default-skin:jar:1.0 skin.
[INFO] Skipped "Source Xref" report, file "xref/index.html" already exists for the English version.
[INFO] Generating "Source Xref" report    --- maven-jxr-plugin:2.5
[INFO] Generating "Dependency Convergence" report    --- maven-project-info-reports-plugin:2.8
[INFO] Generating "Dependency Information" report    --- maven-project-info-reports-plugin:2.8
[INFO] Generating "About" report    --- maven-project-info-reports-plugin:2.8
[INFO] Generating "Plugin Management" report    --- maven-project-info-reports-plugin:2.8
[INFO] Generating "Project Plugins" report    --- maven-project-info-reports-plugin:2.8
[INFO] Generating "Project Team" report    --- maven-project-info-reports-plugin:2.8
[INFO] Generating "Project Summary" report    --- maven-project-info-reports-plugin:2.8
[INFO] Generating "FindBugs" report    --- findbugs-maven-plugin:3.0.1
[INFO] Generating "PMD" report    --- maven-pmd-plugin:3.4
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
.....

The “site” is created in the target/site dir. Opening the file target/site/index.html with the browser, we will see something like the following:

2015-06-08bIn the Project Reports section you will find the FindBugs report like the following:

2015-06-08c   The report is interesting because it give us the complete picture of our project in the summary section but also the list of the files which have FindBugs detected issues with all details including the priority (i.e. how dangerous could be the problem).

Configuration options

Regardless the used mode, the FindBugs plugin can be configured to modify and tune its behaviour. All below configurations should be placed in the configuration section of the plugin:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>findbugs-maven-plugin</artifactId>
    <version>3.0.1</version>
    <configuration>
        .....put here
    </configuration>
</plugin>

warning48
Configurations in the build and reporting sections are independent so we can have two different settings in the two modes, stand-alone and site.

Best practice is to use a light and fast configuration on the standalone mode, in order to run quick checks during development and an high effort, production quality configuration for the site mode.

Let’s examine the most important configuration options:

DEBUG: With <debug>true</debug> option, FindBugs will show what is doing during analysis. Useful to fine tune or checks the settings.

EFFORT: with  <effort>Max</effort> we can increase the time FindBugs is allowed to use to analyze the code. More effort means more accurate analysis but, of course, FindBugs run slower.

EXTERNAL PLUGINS: FindBugs can be extended with additional detectors. See my post on the topic. The  <pluginList>plugin1[, plugin2…]</pluginList> configuration option list the plugins to be used. See the sample project for a real example.

EXCLUSIONS: If you need to disable a detector or to disable the analysis on same classes, an exclude list can be specified with the <excludeFilterFile> path_to_exclude_file </excludeFilterFile> option. File syntax is documented here. An easy way to create this file is by using the FindBugs GUI which has an export filter function. See the sample project for a real example.

 

FindBugs plugins

FindBugs is a key code quality tool for Java based projects.

It includes several dozens of bug patterns which are used by FindBugs to identify potential bugs and, more in general, weaknesses in our code.

FindBugs has a plugin architecture which can be used to extend the set of detectors (bug patterns) used during the analysis.

There are few open source projects which aim to develop FindBugs plugin.

My preferred one is Fb-Contrib which contains a significant amount of additional detectors. See here for the complete list. Most of them are really useful to detect poor code quality.

Another interesting plugin is Find Security Bugs; the focus here is on security vulnerabilities (list here) like using unsecured random generator or not checking data received from the user.

Let’s have a look at versions dependencies:

JDK FindBugs FB-Contrib Find Security Bugs
7 and 8 3.x 6.x 1.3 and above
5 and 6 2.x 5.x 1.2

All plugins are released in .jar format and they can be easily added to the FindBugs :

  • FindBugs stand-alone: place the jar in the plugins dir inside FindBugs installation dir
  • Eclipse FindBugs plugin: use the plugin options to specify the plugin path or place the jar file inside FindBugs plugins dir
  • NetBeans FindBugs integration: use Custom FindBugs Plugins button inside Editor → Hints → FindBugs page.
  • IntelliJ FindBugs plugin: add new plugin in the Plugin Configuration tab.

After adding new plugins, review the list of detectors enabled. New detectors are usually added but not enabled.

My 5 favorite NetBeans IDE features (vs. Eclipse)

In my current project, Eclipse Kepler has been selected by the customer as the preferred IDE for all main tasks. Of course, I’m also using NetBeans 8.0 for some activities like, for example, legacy code test coding.

I have already used Eclipse some years ago and, at that time (2009), Eclipse Galileo were quite superior to NetBeans 6.x: faster, rich of code hints and warnings, full of useful plugins. So, I was not really happy when the project moved to NetBeans (because of its Swing support).

Now let me compare them again, Eclipse Kepler 4.3 vs NetBeans 8.0, on same project (a big ant based web project) and on the same PC (Win7 64bit 4G RAM).

Speed

My first big surprise. Five years ago, when I moved from Galileo to NetBeans I really missed Eclipse speed: coding, windowing, compilation, everything were significantly faster in Eclipse.

Now this speed advantage is gone. I don’t have benchmarks to demonstrate my feeling but now my coding is visibly faster with NetBeans 8.0 than with Eclipse Kepler. If you install Findbugs and PMD plugins, Eclipse becomes really slow.

Native testing support

Second surprise. Why, in 2014, an IDE for Java EE developers does not include full testing support ?

I cannot imagine any real EE developers which does not need any kind of test support from the IDE.

In NetBeans there is complete native jUnit and TestNG support (templates, test runs, jump to test option in the editor, both *Test and *IT files support) which I found very useful and efficient.

Eclipse native support is poor but of course it can be extended via plugin. I’m using MoreUnit plugin which is quite nice and add many missing features to Eclipse. See here for a small tutorial.

But even if you install a test support plugin like MoreUnit, still an important Eclipse limitation remains : the possibility to have a dir for test files separated from the main src dir, which is an essential feature to let us handling deployments. If you don’t believe me, have a look at this 2008 bug still open: https://bugs.eclipse.org/bugs/show_bug.cgi?id=224708 and its related bugs.
Simply unbelievable.

Java Hints and FindBugs integration

While coding, it is important to have feedbacks from the IDE about the quality of the code and possible errors. NetBeans Java hints is a very reach set of the code checks which helps me to avoid stupid errors and keep high the quality of my work. In addition, NetBeans includes first class support for FindBugs which, again, helps me during coding sessions.

I’ve found NetBeans code quality checks support better than the one available in Eclipse Kepler + FindBugs plugin. More checks and better control on how, for example, disable a FindBugs warning on a line (because it is ok) using an annotation: just a click with NetBeans and by hand with Eclipse.

Coding support: templating, completion,interfaces…

I like NetBeans coding support: code templates (some letters + tab to get most used keywords and structures (see here for some examples), auto-completion, now also using sub words, I can easily jump from an interface to its implementation, the new indent guide lines.

Similar features are available in Eclipse using, for example, the Code Recommenders plugin but I’ve found it quite slow on my machine so I had to disinstall it.

User Interface

NetBeans user interfaces has improved a lot while keeping its strong point: it is easy to use. Windows and menus are easy to find and the full screen mode is very useful on small screens. I really like the color coding setup (Norway Today) which makes code reading a pleasure and the dark skins for working late in the evening.

On the contrary, Eclipse interfaces remains somehow confusing and, my personal opinio, perspectives and workspaces are simply a waste of time.

Tutorial: using Lombok to reduce boilerplate code in Java

Lombok (http://projectlombok.org) is a very useful Java annotation library which help us to:

  • reduce boilerplate code
  • code faster
  • avoid stupid errors like forgetting the getter and setter for a new added field.

Lombok can create getter, setters, toString, constructor, equals and hashCode methods automatically. You just need to write the class and add the fields.

Lombok provides several annotations (see http://projectlombok.org/features/index.html for the complete list) but, in my projects, I usually use few of them and, in this tutorial I will show which and why.

To better show Lombok benefits, let’s start with a very simple (Maven based) project. You can find complete project source code here.

We should first define Lombok Maven dependency in the pom.xml file:

 <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.12.4</version>
    <scope>provided</scope>
</dependency>

Important: scope should be provided because we do not want Lombok jar file to be included in our build file.

Let’s now add to a project a very basic class without using any Lombok annotations.


public class WithoutLombokAnnotations {
    private boolean flag;
    private final int number;
    private final String text;
    private List<String> strList;

    public WithoutLombokAnnotations(int number, String text) {
        this.number = number;
        this.text = text;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public List<String> getStrList() {
        if (strList == null) {
            strList = new ArrayList<>(128);
        }
        return Collections.unmodifiableList(strList);
    }

    public void setStrList(List<String> strList) {
        this.strList = strList;
    }

    public int getNumber() {
        return number;
    }

    public String getText() {
        return text;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 11 * hash + this.number;
        hash = 11 * hash + Objects.hashCode(this.text);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final WithoutLombokAnnotations other = (WithoutLombokAnnotations) obj;
        if (this.number != other.number) {
            return false;
        }
        if (!Objects.equals(this.text, other.text)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "WithoutLombokAnnotations{" + "flag=" + flag + ", number=" + number + ",
            text=" + text + '}';
    }
}

Comments on the above code:

  • The class has four fields, two of them are final so we need a constructor.
  • The flag field is a boolean so the getter is named isFlag() and not getFlag()
  • The strList getter method initialize the list if necessary
  • The strList is not included in the toString method
  • Equals and hashCode are calculated on number and text fields only.

A lot of boilerplate code for a very simple class.

Let’s reduce it with the help of Lombok annotations.

GETTER and SETTER

First step: the getters and setters (http://projectlombok.org/features/GetterSetter.html). In our example, all of them are very simple and standard except the strList getter. We can use Lombok’s @Getter and @Setter annotations to remove all standard getters and all setters from our code and have Lombok to generate them during compilation.

Our class code becames:

@lombok.Getter
@lombok.Setter
public class GettersAndSetters {
    private boolean flag;
    private final int number;
    private final String text;
    private List<String> strList;

    public GettersAndSetters(int number, String text) {
        this.number = number;
        this.text = text;
    }

    public List<String> getStrList() {
        if (strList == null) {
            strList = new ArrayList<>(128);
        }
        return Collections.unmodifiableList(strList);
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 11 * hash + this.number;
        hash = 11 * hash + Objects.hashCode(this.text);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final GettersAndSetters other = (GettersAndSetters) obj;
        if (this.number != other.number) {
            return false;
        }
        if (!Objects.equals(this.text, other.text)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "WithoutLombokAnnotations{" + "flag=" + flag + ", number=" + number + ",
            text=" + text + '}';
    }
}

Great. Same functionality and 18 lines of code removed. We left in the code the strList getter because it has non standard functionality.

By using Lombok’s @Getter and @Setter annotation at class level we ask Lombok to generate getter and setter for all non-static fields. Of course final fields will not have setters.

@Getter and @Setter annotations can be placed at class level (like in the example) and at field level.

I usually prefer to use Lombok annotations at class level for two reasons: to not forget to add proper annotations when I add new fields in the class and to avoid to clutter class code with too many annotations (at fields level; see for example when you are using Lombok in JPA entity classes).

 

CONSTRUCTORS

Second step: the constructor (http://projectlombok.org/features/Constructor.html). By adding the @lombok.RequiredArgsConstructor annotation at class level, we can remove the 4 lines of the constructor.

Lombok has three different annotations for the constructors:

  • @RequiredArgsConstructor, the one we used in the example, generates a constructor for all final fields, with parameter order same as field order
  • @NoArgsConstructor creates an empty constructor.
  • @AllArgsConstructor creates a constructor for all fields

 

HASHCODE and EQUALS

Third step: the hashCode and equals methods (http://projectlombok.org/features/EqualsAndHashCode.html). In the example we have created these methods using the number and text fields.

By adding the @lombok.EqualsAndHashCode(of = {“number”, “text”}) annotation we can remove Equals and HashCode methods, going down by other 26 lines of code. And Lombok also generates the canEqual method. See here (http://www.artima.com/lejava/articles/equality.html) for the reasons.

Important: never use @lombok.EqualsAndHashCode without specifying the fields. Always use the of parameter (or the opposite exclude parameter). Without parameters, @lombok.EqualsAndHashCode will use all fields, possibly creating heavy equals and hashCode methods.

 

TOSTRING

Last step is to use @lombok.ToString (http://projectlombok.org/features/ToString.html) to implement the toString method. While having a toString method is highly suggested (for ex. for debugging and analysis purposes), my usual approach is to avoid collections and arrays fields in the toString method to avoid cluttering logs and messages windows with too long text lines.

After all our changes, the original class is now:

@lombok.Getter
@lombok.Setter
@lombok.RequiredArgsConstructor
@lombok.EqualsAndHashCode(of = {"number", "text"})
@lombok.ToString(exclude = "strList")
public class EveryThing {
    private boolean flag;
    private final int number;
    private final String text;
    private List<String> strList;

    public List<String> getStrList() {
        if (strList == null) {
            strList = new ArrayList<>(128);
        }
        return Collections.unmodifiableList(strList);
    }
}

Very short and with just the relevant code shown: the fields and the custom getter. No boilerplate code. Exactly what we were looking for.

LOGGING

Another quite useful Lombok annotation is related to logging (http://projectlombok.org/features/Log.html): Lombok can create a Logger field, named log, in our class. There are six variations of the annotation depending on the logging framework (java, apache, log4j, slf4j,,,).

The logging annotation does not reduce the amount of code in our class (one line for the annotation vs. one line for the manual logger definition) but it can help us to guarantee that we are using the same logging framework among all project classes. In some projects with large teams, I’ve found different logging frameworks used simply because different programmers were using IDE auto-complete functions in a different way.

Having something like

@Slf4J
public class ….

help us to use the same logger on all the class files.

@DATA

The @lombok.Data annotation (http://projectlombok.org/features/Data.html) is a short version for @Getter @Setter @EqualsAndHashCode, @ToString and @RequiredArgsConstructor.

At first glance it looks very convenient (one annotation instead of five) but, unfortunately, we cannot specify the fields for @EqualsAndHashCode and @ToString so it is not a good idea to use the @Data annotation unless the class is really a very simple POJO, without complex fields.

Moreover @Data does not generated automatically a no args constructor, required by JavaBeans and XML conversions so a @NoArgsConstructor annotation is also needed.

SUGGESTIONS

Lombok is very useful to reduce boilerplate code but it is important to keep in mind what Lombok is doing on our code to avoid useless and heavy generated code.

My personal suggestions:

  1. Always specify the fields you want in @EqualsAndHashCode or, in another words, never use the default @EqualsAndHashCode behaviour (all fields). Very important if the class is a JPA entity, where generated ID field should not be used for equality.
  2. Always exclude heavy fields (collections, arrays) in @EqualsAndHashCode and @ToString unless it is really necessary.
  3. Use @NoArgsConstructor when an empty constructor is needed (like for XML conversions and in JavaBeans)
  4. Never use Lombok @Data. As mentioned before, with the @Data annotation we cannot specify the fields for @EqualsAndHashCode and @ToString (see previous suggestions).
  5. On JPA entities:
    @EqualsAndHashCode: exclude generated @Id fields (persistence equality constraint: a generated ID field is not always defined during entity life-cycle.)
    @ToString: exclude other JPA entities and relationship to reduce method weight and avoid unexpected persistence problems like lazy initialization exceptions.

 

FINDBUGS and PMD

FindBugs works well with Lombok because it analyses the compiled code, so after Lombok.

On the contrary PMD, which analyses the source code, does not work well. I suggest to add a @SuppressWarnings(“PMD”) annotation at class level to tell PMD to completely skip the class. See here (http://pmd.sourceforge.net/pmd-5.1.0/suppressing.html) for more details.

 

DELOMBOK

Lombok provides a “delombok” (http://projectlombok.org/features/delombok.html) function so we can look at the code is really compiled, after Lombok processing.

We should open a terminal window, go to the project root dir and run the following command:

java -jar <path_to_lombok.jar> delombok src -d src-del

where

  • <path_to_lombok.jar> is the path to the lombok jar file. Maven users can find it at <maven_repository>/org/projectlombok/lombok/<version>/lombok-<version>.jar
  • src is the name of source dir (Maven default)
  • src-del is a name of the dir where we want delombok to put all files.

Our delombok-ed file is

public class ToString {
    private boolean flag;
    private final int number;
    private final String text;
    private List<String> strList;

    public List<String> getStrList() {
        if (strList == null) {
            strList = new ArrayList<>(128);
        }
        return Collections.unmodifiableList(strList);
    }

    @java.lang.SuppressWarnings("all")
    public boolean isFlag() {
        return this.flag;
    }

    @java.lang.SuppressWarnings("all")
    public int getNumber() {
        return this.number;
    }

    @java.lang.SuppressWarnings("all")
    public String getText() {
        return this.text;
    }

    @java.lang.SuppressWarnings("all")
    public void setFlag(final boolean flag) {
        this.flag = flag;
    }

    @java.lang.SuppressWarnings("all")
    public void setStrList(final List<String> strList) {
        this.strList = strList;
    }

    @java.beans.ConstructorProperties({"number", "text"})
    @java.lang.SuppressWarnings("all")
    public ToString(final int number, final String text) {
        this.number = number;
        this.text = text;
    }

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (!(o instanceof ToString)) return false;
        final ToString other = (ToString)o;
        if (!other.canEqual((java.lang.Object)this)) return false;
        if (this.getNumber() != other.getNumber()) return false;
        final java.lang.Object this$text = this.getText();
        final java.lang.Object other$text = other.getText();
        if (this$text == null ? other$text != null : !this$text.equals(other$text)) return false;
        return true;
    }

    @java.lang.SuppressWarnings("all")
    public boolean canEqual(final java.lang.Object other) {
        return other instanceof ToString;
    }

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public int hashCode() {
        final int PRIME = 277;
        int result = 1;
        result = result * PRIME + this.getNumber();
        final java.lang.Object $text = this.getText();
        result = result * PRIME + ($text == null ? 0 : $text.hashCode());
        return result;
    }

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public java.lang.String toString() {
        return "ToString(flag=" + this.isFlag() + ", number=" + this.getNumber() + ", text=" +
            this.getText() + ")";
    }
}

Improving NetBeans FindBugs integration

In NetBeans 7.4, the FindBugs integration inside the IDE has been improved giving us the possibility to specify additional rules to be checked.

FindBugs has a plugin architecture that let user to add custom rules sets. I usually add FindBugs rules from FBContrib, a FindBugs auxiliary rules set.

There are hundred of rules included in FBContrib. Complete list can be found here.

What is amazing in NetBeans is that not only you can tell the IDE to use the additional rules sets but that the new rules are seamless integrated in the NetBeans interface. For example, FBContrib rules are grouped together with standard FindBugs checks.

First step is to specify the path to the FBContrib jar file (downloaded from the FBContrib website) in the FindBugs configuration (Options –> Editor –> Hints –> FindBugs )

2013-10-22b-fig1

Press “Custom FindBugs Plugins” button.

2013-10-22b-fig2

The “Add” button let us specify one or more additional jar files.

As I already mentioned, the rules included in the FBContrib jar file are integrated with the other native FindBugs rules.

See for example, the rules related to the “Secure” description before

2013-10-22b-fig3

and after FBContrib has been specified:

2013-10-22b-fig4

Both native and FBContrib rules can be enabled and disabled one by one.

With NetBeans Hints, FindBugs and, may be, PMD (see this post on PMD plugin ) all enabled there is a good chance that one problematic line can have more than one warning. In this case I suggest to keep native NetBeans hints enabled and disable the check in the other tools, mainly because NetBeans native checks are faster.

Findbugs integration in NetBeans

FindBugs is a “must-use” tool for any serious Java programmer to detect real and potential critical bugs in our code.

There are two different ways to use FindBugs:

  • while we are coding (inside the IDE) and
  • as a step in the project quality checks phase (usually via a maven plugin).

NetBeans support both usages.First of all, we need to enable the plugin “FindBugs Integration” in “Tools –> Plugins”

FindBugs Integration plugin

FindBugs Integration plugin

FindBugs analysis configuration is available in Tools –> Options –> Editor –> Hints.

Select FindBugs in the “Language” drop-down menu.

FindBugs Options Window

FindBugs Options Window

In this window, there is a “Run FindBugs in Editor” check-box. If enabled, FindBugs will be executed on the .java file we are working on (just after the save).

Note: FindBugs works on .class files and not on source (.java files).

FindBugs can be executed on the complete project using the Source –> Inspect function:

Inspection Dialog

Inspection Dialog

The results of the FindBugs analysis on the complete project is reported in the “Inspector” window:

Inspector Window

Inspector Window

Important:

  • Editor level analysis is done on a single file and with minimal FindBugs effort (to avoid to slow down the editor)
  • Inspection analysis is done on all project class files with deeper FindBugs effort. In this case, it is acceptable to have a slower but more accurate analysis.

The two different analysis can bring to different violations detected.