Groovy

Groovy is an agile and dynamic language for the Java Virtual Machine. Since OpenXava 4m6 you can write the code of your applications using Groovy instead of Java. Even you can combine both languages in the same application.

Configuring Eclipse

The easiest way to work with Grooy in OpenXava is using the Groovy Eclipse plugin. Follow these instructions from Groovy site to install the plugin in Eclipse.
If you're using an OpenXava version previous to v5.0 go to Preferences > Groovy > Compiler and choose Groovy Compiler 1.8.
You have to give Groovy nature to your OpenXava project: click the right button in your project, and choose Configure > Convert to Groovy project. Projects created between OpenXava v4m6 and v5.9.1 have Groovy nature by default.
From now on, you can add Groovy classes to your project, or convert some of your Java classes into Groovy classes, and it just will work. When you press Ctrl-B the .groovy classes will compile into .class automatically.

From command line

If you work from command line, instead of using Eclipse, add a new compile ant target to the build.xml of your project:
<!--
Compile all .java and .groovy
Not needed to call it if you work inside Eclipse
-->
<target name="compile">
  <ant antfile="../OpenXava/build.xml" target="compile"/>
</target>
And remember to call it before calling another target:
$ ant compile deployWar
The compile ant target is included by default in the new projects.

Some sample code

You can write the JPA entities with Groovy:
package org.openxava.test.model;
 
import javax.persistence.*
 
import org.openxava.annotations.*
import org.openxava.calculators.*
import org.openxava.jpa.*
 
@Entity
@Table(name="TOrder")
@View(members="""
    year, number, date;
    customer;
    details;
    amount;
    remarks
    """
)
 
class Order extends Identifiable {
 
    @Column(length=4)
    @DefaultValueCalculator(CurrentYearCalculator.class)
    int year
 
    @Column(length=6)
    int number
 
    @Required
    @DefaultValueCalculator(CurrentDateCalculator.class)
    Date date
 
    @ManyToOne(fetch=FetchType.LAZY, optional=false)
    @ReferenceView("Simplest")
    Customer customer
 
    @OneToMany(mappedBy="parent", cascade=CascadeType.ALL)
    @ListProperties("product.number, product.description, quantity, product.unitPrice, amount")
    Collection<OrderDetail> details = new ArrayList<OrderDetail>()
 
    @Stereotype("MEMO")
    String remarks
 
    @Stereotype("MONEY")
    BigDecimal getAmount() {
        BigDecimal result = 0
        details.each { OrderDetail detail ->
            result += detail.amount
        }
        return result
    }
 
    @PrePersist
    void calculateNumber() throws Exception {
        Query query = XPersistence.getManager()
            .createQuery("select max(o.number) from Order o " +
                    "where o.year = :year")
        query.setParameter("year", year)
        Integer lastNumber = (Integer) query.getSingleResult()
        this.number = lastNumber == null?1:lastNumber + 1
    }
 
}
 
Or the actions:
package org.openxava.test.actions
 
import org.openxava.actions.*
 
class ChangeYearConditionAction extends TabBaseAction {
 
    int year;
 
    void execute() throws Exception {
        tab.setConditionValue("year", year)
    }
 
}
Even the test cases:
package org.openxava.test.tests
 
import javax.persistence.*
 
import org.openxava.tests.*
import org.openxava.util.*
 
import com.gargoylesoftware.htmlunit.html.*
 
import static org.openxava.jpa.XPersistence.*
 
class OrderTest extends ModuleTestBase {
 
    OrderTest(String testName) {
        super(testName, "Order")
    }
 
    void testCalculatedPropertiesFromCollection_generatedValueOnPersistRefreshedInView()
        throws Exception
    {
        String nextNumber = getNextNumber()
        execute("CRUD.new")
        assertValue("number", "")
        setValue("customer.number", "1")
        assertValue("customer.name", "Javi")
        assertCollectionRowCount("details", 0)
        execute("Collection.new", "viewObject=xava_view_details")
        setValue("product.number", "1")
        assertValue("product.description", "MULTAS DE TRAFICO")
        assertValue("product.unitPrice", "11.00")
        setValue("quantity", "10")
        assertValue("amount", "110.00")
        execute("Collection.save")
        assertNoErrors()
        assertCollectionRowCount("details", 1)
        assertValue("amount", "110.00")
        assertValue("number", nextNumber)
        execute("CRUD.delete")
        assertNoErrors()
    }
 
    void testDoubleClickOnlyInsertsACollectionElement() throws Exception {
        execute("CRUD.new")
        setValue("customer.number", "1")
        assertCollectionRowCount("details", 0)
        execute("Collection.new", "viewObject=xava_view_details")
        setValue("product.number", "1")
        setValue("quantity", "10")
        HtmlElement action = getForm().getElementById(decorateId("Collection.save"))
 
        action.click() // Not dblClick(), it does not reproduce the problem
        action.click()
        Thread.sleep(3000)
 
        assertNoErrors()
        assertCollectionRowCount("details", 1)
 
        execute("CRUD.delete")
        assertNoErrors()
    }
 
    private String getNextNumber() throws Exception {
        Query query = getManager().
            createQuery(
                "select max(o.number) from Order o where o.year = :year")
        query.setParameter("year", Dates.getYear(new Date()))
        Integer lastNumber = (Integer) query.getSingleResult()
        if (lastNumber == null) lastNumber = 0
        return Integer.toString(lastNumber + 1)
    }
 
}

Beware with nested annotations

Most of the Java syntax is compatible with Groovy. However, there is a litte detail of Groovy syntax that you have to take into account when using nested annotations: you have to use [] instead of {}, as following:
@Views([ // Use [ instead of {
    @View(members=""" // Use """ for multiline strings
        building [
            name, function;
            address
        ]
    """),
    @View(name="Simple", members="name")
]) // Use ] instead of }
Note how we use @Views([ ... ]) instead of @Views({ ... }). Note also that you can use """ for multiline strings.
For more details about Groovy syntax read Groovy style and language feature guidelines for Java developers.