A few people have been asking me recently to integrate the Wicket project as a view technology for Grails. What is Wicket? It is a component oriented framework kind of like JSF, but unlike JSF it uses convention-over-configuration and doesn't require you to edit reams of XML. So in this sense its aims are more inline with Grails' aims.
So the question was how do we integrate these two frameworks? Well I thought it couldn't be that difficult and as it turns out, it isn't. So what I did was create a plug-in with "grails create-plugin wicket". The next thing I needed were some jars, so I got Wicket 1.2.6, Wicket Extensions 1.2.6 jar and the Wicket Spring integration jars for 1.2.6 and put them in the plug-ins lib directory.
Job done, now we need to set-up a convention that makes sense for both Grails and Wicket:
1) Since controllers are essentially roughly analogous to the Wicket Application object I decided that the convention would be to have a WebApplication.groovy file in the grails-app/controllers directory.
2) Wickets HTML components are like the views, so they can live in the grails-app/views directory
So to follow the HelloWorld example on the Wicket page we end up with something like this being the structure:
So just for clarify the WebApplication.groovy file looks like this:
import wicket.Page;
import wicket.spring.*;
public class WebApplication extends SpringWebApplication
{
public Class getHomePage()
{
return HelloWorld.class;
}
}
Whilst HelloWorld.groovy looks like this:
import wicket.markup.html.WebPage;
import wicket.markup.html.basic.Label;
public class HelloWorld extends WebPage
{
public HelloWorld()
{
add(new Label("message", "Hello World!"))
}
}
Finally, the HelloWorld.html file looks like this:
<html>
<body>
<span wicket:id="message">Message goes here</span>
</body>
</html>
Ok so with that done, let's take advantage of the conventions we have in place here. When I created the plug-in with "grails create-plugin" it also created me a WicketGrailsPlugin.groovy file in the root of the plugin project. Since the normal controller mechanism no longer makes sense we modify the plug-in to "evict" the controllers plug-in when installed:
class WicketGrailsPlugin {
def version = 0.1
def dependsOn = [:]
def evicts = ['controllers'] // evict the controllers plugin
...
}
With that done we now need to configure the Wicket "applicationBean" in Spring. To do this we're going to use the GrailsApplication object and find a class that is an instance of a Wicket Application. This class just happens to be the one we defined earlier in grails-app/controllers:
import wicket.*
class WicketGrailsPlugin {
...
def doWithSpring = {
def applicationClass = application
.allClasses
.find { Application.class.isAssignableFrom(it) }
if(applicationClass) {
applicationBean(applicationClass) // defines the spring bean
}
}
}
We're of course using the Spring BeanBuilder here to define the bean definition if the Spring context. Ok job done.
Now we need to modify web.xml.. but wait. In Grails even web.xml is created on the fly so other plugins can participate in its generation. So we can do this in the plug-in file again:
import wicket.*
class WicketGrailsPlugin {
...
def doWithWebDescriptor = { xml ->
def servlets = xml.servlet[0]
servlets + {
servlet {
'servlet-name'('wicket')
'servlet-class'('wicket.protocol.http.WicketServlet')
'init-param' {
'param-name'('applicationFactoryClassName')
'param-value'('wicket.spring.SpringWebApplicationFactory')
}
'load-on-startup'(1)
}
}
def mappings = xml.'servlet-mapping'[0]
mappings + {
'servlet-mapping' {
'servlet-name'('wicket')
'url-pattern'('/app/*')
}
}
}
}
Here we use Groovy's XmlSlurper DSL to modify and the XML simply by using the + operator and some Groovy mark-up. We again use the Wicket Spring integration support to get it all working, so when Wicket loads it will actually look for the bean created by the Grails plug-in. And that's it, to test the plug-in we can type "grails run-app" and navigate to http://localhost:8080/grails-wicket/app and we see:
Not hugely impressive I know, but what it does show is Wicket running as the view tech in Grails which means that Wicket components and applications can use GORM to simplify the ORM layer of Wicket applications and other features of Grails like Services and Jobs. This took me all of 20 minutes to write, in fact I've spent longer writing this article than the plug-in. Not that it is complete of course, things left to do are to add reloading support to the Groovy files that Wicket users. Advice from the Wicket community on how to do that would be appreciated. And of course I haven't tested it against any more complex examples yet.
Now of course it wouldn't be a plug-in if it couldn't be installed into other apps. To do this we package it up with "grails package-plugin" which will create a zip. I've placed in plug-in at http://dist.codehaus.org/grails-plugins/ so in any Grails application you can now do:
grails install-plugin Wicket 0.1
or directly from the URL:
grails install-plugin http://dist.codehaus.org/grails-plugins/grails-Wicket-0.1.zip
And it will install this version of the plug-in.
Note: The plug-in doesn't work with the Grails URL mapping mechanism in 0.5 so you need to delete any grails-app/conf/*UrlMappings.groovy files otherwise you'll get an exception.
Plug-in sources can be found here: http://svn.grails-plugins.codehaus.org/browse/grails-plugins/grails-wicket/trunk