Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Groovify CUBA Platform — An Overview of Groovy

DZone's Guide to

Groovify CUBA Platform — An Overview of Groovy

A review of the benefits of Groovy compared to Java, and how to create simple DSLs.

· Java Zone
Free Resource

Managing a MongoDB deployment? Take a load off and live migrate to MongoDB Atlas, the official automated service, with little to no downtime.

Groovify CUBA - An overview of Groovy

Developing an application in CUBA Platform is mostly about productivity. It has other advantages, but productivity is probably one of the most important reasons. To ramp up productivity on all stages of CUBA app development, we can invite Groovy to the party.

In this first of two blog posts i will give you an overview about Groovy, so you get a understanding that this might be valuable for you. Why? Because in my opinion the productivity advantage of CUBA falls apart when turn from generating the UI or creating the domain model to the part of programming where you actually want to implement business logic. In this case you are back at the POJO model either in your controller logic or in the services that are just Spring beans.

Coming to CUBA from a Groovy background and haven’t developed in Java for quite some time, it feels a little bit clumsy to go back. This is why i think it’s probably worth thinking about raising the productivity gain (in the business logic side of things) just a little bit by getting the different benefits of the Groovy language onto our CUBA application.

The Elevator Pitch for Groovy

Ok, for an elevator pitch, the easiest thing is to come up with is syntax. We as programmers all love to care about syntax, so there you go. A customer class in Java (~100 LOC):

import java.util.Collection;
import java.util.Date;

public class Customer {

    private String firstName;
    private String lastName;
    private Date birthday;
    private Collection<Order> orders;

    public Customer() {}

    public Customer(String firstName, String lastName, Date birthday, Collection<Order> orders) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.birthday = birthday;
        this.orders = orders;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Collection<Order> getOrders() {
        return orders;
    }

    public void setOrders(Collection<Order> orders) {
        this.orders = orders;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", birthday=" + birthday +
                ", orders=" + orders +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Customer customer = (Customer) o;

        if (firstName != null ? !firstName.equals(customer.firstName) : customer.firstName != null) return false;
        if (lastName != null ? !lastName.equals(customer.lastName) : customer.lastName != null) return false;
        if (birthday != null ? !birthday.equals(customer.birthday) : customer.birthday != null) return false;
        return orders != null ? orders.equals(customer.orders) : customer.orders == null;

    }


    public int calculateTurnover() {
        int totalTurnover = 0;

        for(Order order: orders) {
            totalTurnover += order.getAmount();
        }

        return totalTurnover;
    }

    @Override
    public int hashCode() {
        int result = firstName != null ? firstName.hashCode() : 0;
        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
        result = 31 * result + (birthday != null ? birthday.hashCode() : 0);
        result = 31 * result + (orders != null ? orders.hashCode() : 0);
        return result;
    }
}

The equivalent in Groovy (~15 LOC):

import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

@ToString
@EqualsAndHashCode
class Customer {
    String firstName
    String lastName
    Date birthday
    Collection<Order> orders = []

    int calculateTurnover() {
        orders*.amount.sum() ?: 0
    }
}

That’s it. It has the same functionality.

Just in case you think I’m kidding: The Unit test in this article proves that both variants are semantically equivalent.

Groovy basically has a significant better signal to noise ratio. I’m not sure if you noticed it, but there is a little bit of signal in this class. It’s the method calculateTurnover, which is basically the “business logic” if you will. In the Java class it’s very hard to find, because it’s just not visible.

The Differences of Groovy

When comparing both examples, these are some of the things that are different:

  • Removed imports, because of more default imports in Groovy.
  • Removed semicolons where not needed.
  • Changed default visibility scope: private for fields and public for methods, because that’s the defacto default in Java.
  • Removed Getters and Setters, which will be generated by the compiler in the default case.
  • Removed constructors and added a map-based constructor as well as a default one.
  • Removed toString, equals, and hashCode because of the AST-Transformation Annotation.

We could even reduce it further with @Canonical instead of @ToString and @EqualsAndHashCode. A running example of this you’ll find here (which is a very good playground to start with btw.)

Groovy Shines With Maps and Lists

Since bashing about Java Getters and Setters is not my main purpose here, let’s have a look at some more interessting stuff like the implementation of calculateTurnover.

int calculateTurnover() {
    orders*.amount.sum() ?: 0
}

Groovy has some very cool features regarding the Collections API. In the official docs you’ll find a good overview of the features (2. Working with collections). I’ll guide you through a few of them.

Starting with the * Operator. The attribute orders is a collection. When doing a *. on a collection, it will execute the method call after the . for each item in the collection. In this case, getAmount() on every item in the orders list will be called. The result of this is a List with the results of each entry. If you are familiar with functional programming, the Map operation would be something similar (the exact equivalent of it is the collect method in Groovy).

Then, since orders*.amount returns a list, we can call the operation sum on it, which will act according to it’s name. The elvis operator ?: will either return the expression on the left if it’s not null, otherwise it will return the right side.

If this is to scary to you, another alternative implementation would be something like:

int calculateTurnover() {
    def sum = 0
    orders.each {
        sum += it.amount
    }
    sum
}

This example is a little closer to what we saw in the Java class. each is a method on a List, that takes a closure (a function) as a parameter. On each element in the list the function will be executed and it will be the corresponding list item. Same story as Java 8 Lambdas.

There are two additional things that are worth mentioning. First, as I said, each method has one argument: a closure. In Groovy, often times it is not required to use parentheses at all. In this case, it is equivalent to orders.each({ sum += it.amount }) (See the docs — “5. Omitting parentheses” — for more details). Second, return is an optional keyword. If it’s not in place, Groovy will use the last expression of the method as the return value.

Since the headline talks about List and Maps, let’s have a short look at this first class language construct of Groovy. Here we have a few examples on how Groovy handles Maps:

def map = ["hello" : "world", "lorem" : "ipsum"]

assert map["hello"] == "world"
assert map.lorem == "ipsum"

// create a customer with a map constructor
def customer = new Customer([
firstName: "Mario", 
lastName: "David", 
orders: [
new Order(amount: 150)
]
])

The map constructor gives you a cartesian product on the possible constructors. Using a map as the parameter list in general (not just in the constructor) can be a very good idea, because it drastically increases the readability of the code compared to parameter lists where the third and the firth parameter is a boolean, and nobody is able to know what that actually indicates. It literally gives you named parameters like in C# or Javascript (when passing a JSON object to a function).

More differences that differtiates Groovy from Java can be found in the Groovy style guide.

By the way, == is not a reference comparison like in Java. Instead the equals method of the objects are called, because like before: Groovy changes default Java behavior to something that makes sence in most cases.

Syntax is Just Syntax — There Are More Things

Although this syntactic sugar compared to Java is neat, there are other things to keep in mind before switching your whole project from one language to another. Since this discussion would clearly go beyond this blog post, i’ll just go over them and give you some resources to dig down further.

Performance is one of these issues. In the beginning, before invokedynamic was build into the JVM, creating performant dynamic languages on the JVM was pretty hard. Nowadays Groovy gives the user choices about speed with @CompileStatic. More information you’ll find at this InfoQ article from 2012 as well as this SE Radio podcast with Cédric Champeau. But whenever thinking about performance, keep in mind the two rules of software optimization.

Next up, we have dynamic-, static-, strong- and duck-typing. All of these attributes are correct for Groovy. You basically can def all the things if you want to. The runtime will figure out the rest on your behalf. This is potentially not the best thing to do, so Groovy gives you choices. When you want to use types and have that checked by the compiler there are options like @TypeChecked or @CompileStatic. Have a look at this article optional typing in groovy for a few insights.

Aditionally there is a Metaobject Protocol (MOP), which lets you do runtime meta programming and since Groovy is compiled, you can do compile time metaprogramming with AST-Transformations. An example of runtime meta programming is the following example from a Grails (a Groovy based Web framework) Domain class:

class Post{

String content
String teaser
Date dateCreated
Date lastUpdated

static hasMany = [comments: Comment]

}

myPost = Post.findByTeaserLike("%my teaser%")

findByTeaserLike is a method that does not exist at compile time. It is a method that is evaluated at runtime and build and execute a SQL Query that queries for a post entry where the column teaser is like %my teaser%. A good insight on this feature (which is based on methodMissing) you’ll find in the article Groovy Goodness: Create Dynamic Methods.

Based on this meta programming feature comes another one: domain specific languages. You can create languages that look like this:

take 2.pills of chloroquinine after 6.hours

// equivalent to: take(2.pills).of(chloroquinine).after(6.hours)

To create a DSL in Groovy really is a breeze. This is perhaps not directly relevant for the developers (although it could be), doing this right can give your process a dramatic additional performance boost and include other people be part of the process as well. Have a look at the book domain specific languages from Martin Fowler for further reading.

Grooovy Can Make a Difference

To wrap this up, I think you get a good feeling about the differences that Groovy can make. The syntactic sugar together with more options open up possibilities for the users of this language.

As I already wrote last time, software developers paychecks are the driving cost factors in most IT efforts. This is especially true for software development. Any opportunity to get better in this regard will increase the overall outcome. CUBA has helped us with different things to go down this road pretty far. To use Groovy in this scenario is just another one of these steps that focuses on the business part of things.

If you want to take a deeper look into Groovy (and i hope i could encourage you to do so), there a very good book about Groovy, called Programming Groovy 2 form Venkat Subramaniam which goes much deeper in the described topics.

In the second part of this two part blog post, I will go trough the actual integration. We’ll have a look on how to enable Groovy in the CUBA app and take look at some other integration possibilities as well.

MongoDB Atlas is the easiest way to run the fastest-growing database for modern applications — no installation, setup, or configuration required. Easily live migrate an existing workload or start with 512MB of storage for free.

Topics:
java ,groovy ,cuba platform

Published at DZone with permission of Mario David. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}