Sunday, October 28, 2012

Lombok and Spring: IntelliJ Plugin

As indicated in my last post, I've been playing around with Project Lombok a little. One of the easier to absorb features is the @Getter/@Setter annotations, which removes the need to explicitly define getters and setters in your Java classes.

I use IntelliJ as my main IDE. However, when I started to use Lombok, I would get error messages from the IDE when viewing a Spring configuration file that used setter injection.

The answer to this problem was to simply install the lombok-intellij-plugin, which fixed the error rather nicely.

We'll setup the scenario withe simplest of objects that I can think of. If you now have the answer you're looking for (and you probably do), feel free to skip the example below:

    public class Person {

        private String name;

        public Person() {}

        public Person(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

A very simplisitc Spock specification that uses Spring testing to inject an instance of a Person:

    @ContextConfiguration
    class PersonSpec extends Specification {

        @Autowired
        Person person

        def "has a name"() {
            expect: person.name == 'Rudiger'
        }
    }

And the bean that is autowired in from PersonSpec-context.xml:

    <bean id="rudiger" class="prystasj.lombok.plugin.example.Person">
        <property name="name" value="Rudiger"/>
    </bean>

Here the test passes and there is no "red" for errors in our IDE.

Now if we replace the getter and setter in the Person with annotations from Lombok:

    public class Person {

        @Getter @Setter
        private String name;

        public Person() {}

        public Person(String name) {
            this.name = name;
        }
    }

The tests still passes, but our IDE is showing us some red when viewing the Spring configuration file:

At this point, installing the lombok-intellij-plugin sends the error away.

Wednesday, October 24, 2012

Lombok: Using @Getter/@Setter and @Delegate

I've been working on a project for the majority of the past year that's part of a larger architecture where the powers that be have not approved the use of a language like Groovy due to strong feelings about things like type safety, among other fears. I don't want to get into a debate about whether such fears have any foundation (maybe at a later date). I do however want to try to introduce Project Lombok here, which has helped simplify the Java we've been writing, helping to remove some of the boilerplate code that we've been writing, bringing our code a little closer to what it might look like with Groovy.

Lombok has many features to help simplify your Java. For now, I'll present just @Getter/@Setter and @Delegate. We'll compare a plain Java version of some object and present both a Lombok and Groovy version for comparison.

Our example is pretty straightforward, a nameable Rocket that uses a Booster to take off and record if the rocket has been started:

    public class Rocket {

        private final Booster booster;

        private String name;

        public Rocket(String name, Booster booster) {
            this.name = name;
            this.booster = booster;
        }

        public void takeoff() {
            booster.takeoff();
        }

        public boolean isStarted() {
            return booster.isStarted();
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public class Booster {

        private boolean started = false;

        public void takeoff() {
            System.out.println("3.. 2.. 1.. Java Rocket go now!");
            started = true;
        }

        public boolean isStarted() {
            return started;
        }
    }

The above code has some classic Java boilerplate in getters and setters, along with a couple one line methods that acts as simple pass-throughs from Rocket to its Booster collaborator.

Now we can introduce the @Getter/@Setter and @Delegate annotations to simplify the code (with the import statements ommitted for brevity):

    public class Rocket {

        @Delegate private final Booster booster;

        @Getter @Setter private String name;

        public Rocket(String name, Booster booster) {
            this.name = name;
            this.booster = booster;
        }
    }

    public class Booster {

        @Getter private boolean started = false;

        public void takeoff() {
            System.out.println("3.. 2.. 1.. Lombok Rocket go now!");
            started = true;
        }
    }

This code is simpler as the getters and setters have been removed, along with the pass-throughs. What would a more Groovy example look like?

    class Rocket {
        @Delegate private Booster booster
        String name
    }

    class Booster {

        boolean started = false;

        def takeoff() {
            println "3.. 2.. 1.. Groovy Rocket go now!"
            started = true
        }

    }

This code is extremely similar to the Lombok example. The @Getter and @Setter annotations are missing because with Groovy we get them by default, however, now we could conceivably find a way to directly set the started property of the Booster class. Another somewhat significant difference is the Rocket constructor is missing, which we would have to reinstate if we were using this code from Java.

The same Spock specification can be used to test all 3 classes to verify they provide the same behavior:

    class RocketSpec extends Specification {

        def name = "LombokRocket"
        def booster = new Booster()

        def rocket = new Rocket(name, booster)

        def "initially has not taken off"() {
            expect: !rocket.isStarted()
        }

        def "can takeoff"() {
            when: rocket.takeoff()
            then: rocket.isStarted()
        }

        def "has a name that can be changed"() {
            when: rocket.name =  name.reverse()
            then: rocket.name == name.reverse()
        }

    }

When looking at the Lombok version, it is definitely closer to the Groovy version, showing that if you are stuck using Java, there are some features you can still have with a little work.

Another point of interest is getting both Lomboked and Groovy code to compile alongside our Java code using Maven. I've uploaded this project to GitHub if you would like to view the POM and the rest of the source:

I'll look to add to the above project in the future to demonstrate some more Lombok features.

Comment away, and thanks!