Getting Groovy with Seam after TSSJS
April 1, 2008
TSSJS was a great conference this year. Neal Ford, well-known author and ThoughtWorker, initiated the conference by delivering an inspiring keynote address discussing dynamic languages, DSLs (Domain-specific languages), and their role in the evolution of software development. Matt Raible blogged about the presentation and you can find a brief summary here. Polyglot programming became a theme of the conference with Ted Neward delivering yet another inspiring keynote encouraging developers to create their own language.
Building on this inspiration, Scott Davis discussed taking the Groovy Red Pill. Well, you would be happy to know I have taken the Red Pill, but of course with a Seam twist. I have been working on a time-tracking application (for my company’s internal use) utilizing Seam and Groovy that I will release as a freely available example. Groovy and Seam integration will be discussed in-depth in JBoss Seam 2E, but let’s look at the Groovy way to initialize a Timesheet:
@Entity
class GroovyTimesheet
{
@Id @GeneratedValue
Long id;
@OneToMany
@JoinColumn(name="TIMESHEET_ID")
List<GroovyTimeEntry> entries = new ArrayList<GroovyTimeEntry>();
GroovyTimesheet(PayPeriod payPeriod, int month, int year)
{
(payPeriod.getStartDate(month, year)..
payPeriod.getEndDate(month, year)).each
{
entries << new GroovyTimeEntry(hours:0, date:it);
}
}
// ... ...
}
So what is going on here? Essentially we define a range of dates that are iterated over. The PayPeriod is a fairly simple enum that determines the start and end dates of a pay period. By specifying (startDate..endDate) we define a range. Groovy understands the meaning of a range of dates allowing us to express this in a very concise manner (try expressing this in Java and you’ll get the picture). In addition, we use the each operation on this range. The each operation allows us to define a closure that executes as Groovy loops through our range of dates a day at a time. This allows us to initialize each GroovyTimeEntry instance for the pay period.
You will also note use of the << operator (or the leftShift operator). This operator is defined for a List allowing us to add elements to the list through this syntactic sugar. The GroovyTimeEntry instance is initialized using the default construction approach. By default, named constructor parameters can be specified in any order to initialize an object instance. If you define a constructor this is no longer provided by default. Finally, you will notice the use of the keyword it in the closure we defined for the each operation. The keyword it provides the value of the current element in the iteration. So in our instance, as we loop through the date range, each date will be provided iteratively in the range.
@Entity
class GroovyTimeEntry {
@Id @GeneratedValue
Long id;
BigDecimal hours;
Date date;
}
Wow, is that all the code? Looks pretty nice doesn’t it. As mentioned, the default constructor allows us to specify named parameters. In addition, getters and setters are automatically provided for each of our attributes.
You’ve probably noticed the use of JPA annotations here. This is perfectly legal and your groovy class will be a JPA entity. The same is true for Seam components annotated with @Name. So how does this work? Groovy classes are compiled to Java bytecode under the covers so JEE and Seam features are fully available to your Groovy classes at runtime. Simply use the groovyc compiler that can be accessed here or include the groovyc Ant task into your build. This will be covered in-depth in JBoss Seam 2E.
So what if we want to add GroovyTimeEntry instances to the GroovyTimesheet instance programmatically? The following code implements this:
@Entity
class GroovyTimesheet
{
// ... ...
void leftShift(GroovyTimeEntry entry)
{
entries << entry;
}
// ... ...
}
As mentioned, the leftShift operator is provided by Groovy and can be overloaded within your custom implementations. By defining a custom leftShift implementation we are now able to add a GroovyTimeEntry instance through the following:
// ... ... timesheet << new GroovyTimeEntry(new Date()); // ... ...
Operator overloading isn’t limited to the leftShift operation. We can also overload other operators such as +:
// ... ...
BigDecimal plus(GroovyTimeEntry entry)
{
this.hours + entry.hours;
}
// ... ...
This allows us to add the hours of two GroovyTimeEntry instances using the simple + operator. Notice that a return is not specified. This is optional as the last line is assumed to be a return statement.
As mentioned, a complete time-tracking application will be provided that not only demonstrates Groovy entities but also Groovy Seam components. In addition, the time-tracking application will make use of the ExpandoMetaClass to extend final Java classes at run-time! The code for the application is in development and will be made available through Google code. Also, stay tuned for JBoss Seam 2E which will provide the intimate details of Groovy and Seam integration. So take the Red Pill and use the meta-programming features of Groovy in your own Seam applications!
Posted in
content rss
April 4th, 2008 at 3:09 am
Very interesting stuff. Seam is already quite interesting. Using Groovy with it might make it even more productive.
Two questions:
Did you make any performance tests compared to a Java solution?
Do you know if there are plans to integrate Groovy support in the JBoss Tools for Eclipse?
Looking forward to the 2nd edition of the book. The first one was great to give me an overview of Seam.
April 6th, 2008 at 3:47 pm
No, I have not as the solution is only implemented in Groovy. There would be a difference, I’m sure, but for most applications this shouldn’t be a problem. If you would like to get a comparison, the Seam Booking example has been implemented as a basic Java solution and a Groovy solution. You can find each in the Seam distribution. Feel free to run performance tests and report back your results. I’m sure many readers would be interested!
There are not that I am aware of, but the Groovy plugin works nicely with JBoss Tools and is what I have been using for development. There are some issues with the Groovy plugin but it has come a long way
April 7th, 2008 at 2:22 am
Quite interesting, Yeah, implementing domain models using groovy’s additional constructs will be elegant.
April 14th, 2008 at 8:11 pm
The standard wisdom from the Groovy camp is when performance becomes a huge issue dropping down into Java is the solution. Since Groovy has numerous ways of seamlessly (pun intended) integrating with Java it shouldn’t be a problem in most cases to do just that.
On a side note Groovy 1.5.5 was just announced with a host of performance improvements.
April 16th, 2008 at 2:25 pm
Could you show me your configuration of groovy plugin and jboss-tools? Is hot deployment working for groovy classes? I’ve created groovy componente annotated with @Name but it is not loaded by seam…
April 21st, 2008 at 12:09 am
Jacob,
Excellent overview and article, my friend. A few points to point out:
1. The latest Groovy 1.5 version allows use of annotations but yet does not support definition of custom annotations. I hope to see that feature soon in a future version of Groovy.
2. By default, all Groovy closures have an implicit it parameter if the closure parameters were not declared. However, you can add multiple parameters by separating them with commas.
3. Groovy has a 1:1 mapping between an operator (+, -, *..) to an operator method. For example, by overriding plus function in a Groovy class, you are overriding the + operator for your class.
4. Range data types can handle both forward ranges (min…max type) or reverse ranges (max..min).
On the plugin/jboss-tools question: Groovy plugin has to be configured separately from JBoss Tools plugin. Groovy Eclipse plugin is fully functional and supports all groovy features including syntax coloring, highlighting, debugging of Groovy files within Eclipse. Groovy Plugin works with eclipse 3.2 but you need a minimum of Eclipse 3.3 to get JBoss Tools to work.