While I'm waiting for the vote on Tapestry 5.0.12, I'm starting some work on a more complete demo application. As usual, I'm trying to teach myself a bunch of new things, such as using Ant and Ivy (in place of Maven), and using Groovy for my pages, entities and services.
I'm still having trouble with live class reloading in Tomcat, so I'm staying with Jetty for a bit longer.
The Groovy stuff for pages and such is just fine. My demo application is a simple blogging site (boring stuff, but a domain model everyone understands). I don't have much yet, but my first page lists recents blog postings:
<t:grid source="recentPostings"/>
And the page class is written in Groovy:
package com.nfjs.hls.blog.pages
import org.apache.tapestry5.ioc.annotations.Inject
import org.hibernate.Session
class Index
{
@Inject
Session session
def getRecentPostings()
{
session.createQuery("from Posting order by posted desc").setMaxResults(20).list()
}
}
A few notes; the session gets injected even though the property is not private; Groovy is creating a private instance variable and a getter/setter for the property. That's just perfect.
I also like how succinct the getRecentPostings() method is. In previous apps, I tended to create a DAO service to hide some of the Java Generics ugliness, but that just stops being an issue here. With IDEA, I still get most of my editor support as well.
Tapestry has no knowledge of Groovy; IDEA is compiling the .groovy files to .class files and Tapestry is loading them. That's the beauty of the JVM.
For this simple example, we could write Java code that is nearly as succinct, but going forward I see no reasons why the Groovy code will not be significantly shorter, simpler and more readable than the equivalent Java code.
There's no reason to limit the use of Groovy to the pages and components.
Entity classes in Groovy are also nice:
package com.nfjs.hls.blog.entities
import javax.persistence.*
import org.apache.tapestry5.beaneditor.DataType
import org.apache.tapestry5.beaneditor.NonVisual
import org.apache.tapestry5.beaneditor.ReorderProperties
import org.apache.tapestry5.beaneditor.Validate
@Entity
@ReorderProperties ("title,posted,content")
class Posting
{
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
@NonVisual
Long id
@ManyToOne (optional = false)
Blog blog
@Column (nullable = false)
@Validate ("required")
String title;
@Column (nullable = false)
@Validate ("required")
@DataType ("longtext")
String content
@Column (nullable = false)
Date posted
}
The @Validate annotations are Tapestry-specific and are being applied to the fields (allowing this is a new feature in 5.0.12). Because the getters and setters are generated without line numbers, the order of the properties ends up being a jumble; the @ReorderProperties annotation puts them into a reasonable order. You win some, you lose some.
On the services side of things, Groovy is a big win with Tapestry IOC. I have an AppModule to configure a few things, such as turning off production mode (which turns on full exception reports), and I want to override part of my Hibernate configuration in development mode:
package com.nfjs.hls.blog.services
import org.apache.tapestry5.SymbolConstants
import org.apache.tapestry5.hibernate.HibernateConfigurer
import org.apache.tapestry5.ioc.MappedConfiguration
import org.apache.tapestry5.ioc.OrderedConfiguration
import org.apache.tapestry5.ioc.annotations.Symbol
class AppModule
{
def contributeApplicationDefaults(MappedConfiguration configuration)
{
configuration.add SymbolConstants.PRODUCTION_MODE, "false"
}
def contributeHibernateSessionSource(OrderedConfiguration configuration,
@Symbol ("tapestry.production-mode")
boolean productionMode)
{
if (!productionMode)
{
configuration.add "DevMode", {conf ->
conf.setProperty "hibernate.show_sql", "true"
conf.setProperty "hibernate.format_sql", "true"
conf.setProperty "hibernate.hbm2ddl.auto", "create"
} as HibernateConfigurer
}
}
}
The ability to wrap a closure as a simple interface (the kind of callback interface used throughout Tapestry) is very, very useful. The only fly in the ointment is GROOVY-2827, which keeps me from using SymbolConstants.PRODUCTION_MODE with the @Symbol annotation on the productionMode parameter.
I think I can get used to the Groovy syntax; I actually prefer the Groovy closure syntax to the Ruby block syntax though I generally prefer the Ruby approach elsewhere.
I expect to write all of this application in Groovy, and that may ultimately become an overall recommendation. Just us crazy framework authors should be coding in pure Java!