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.