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
OpenXava Studio
The easiest way to work with Grooy in OpenXava is using the
Groovy
Eclipse plugin. Given that OpenXava Studio is based on Eclipse, you
can install the plugin on it. In OpenXava Studio go to
Help >
Eclipse Marketplace menu and look for "Groovy". Then install the one
called "Groovy Development Tools":
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, but in OpenXava v6 and v7 you have to add the
Groovy nature explicitly.
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
save a
.groovy class it will compile into
.class
automatically. You can put your Groovy code in
src/main/java
folder of your project for OpenXava v7 or in any source folder in previous
version of OpenXava.
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.model.*
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 about
Groovy
differences with Java.