Thinwire, which is an open source Ajax framework, was one of the more interesting technologies that I encountered at JavaOne. It provides a Swing-like API for building a web GUI. Your application code, which runs in the web container, constructs a GUI component hierarchy, which is then rendered by an Ajax client in the browser. See the website for an example. Last year, I helped develop the server-side part of a similar framework along with a demo application. I found it to be an extremely compelling way of building a web application: nice server-side Java code with no messy HTML or JavaScript. It’s great to see that Thinwire are offering something similar.
In order to see whether Thinwire lives up to it’s promise I decided to build a Thinwire front-end to Project Track , which is a sample JSF, Spring, Hibernate and Acegi application developed by myself and Kito Mann. To make it a little more interesting I decided to use Groovy as the implementation language.
A major part of using Thinwire consist of building hierarchies of GUI components using code like this
Dialog dialog= new Dialog(..)
Button button = new Button(...)
dialog.getChildren().add(button)
One of the neat things about Groovy is that has the concept of builders, which can simplify this kind of code. Here is what I came up with:
class ThinwireBuilder extends BuilderSupport {
def components = new ComponentContainer()
public Object createNode(Object name, Map attributes) {
def component = attributes.type.newInstance(attributes.label)
components.put(name, component)
if (attributes.bounds) {
component.setBounds(*attributes)
}
if (attributes.click) {
component.addActionListener("click", new MyActionListener(attributes.click));
attributes.click.delegate = components
}
return component
}
public void setParent(parent, child) {
parent.getChildren().add(child);
}
....
}
The createNode() method instantiates a component using the attributes passed in as a Map (attributes.x is shorthand for attributes.get(”x”)). The builder also lets the click listener for a button be a Groovy closure, which eliminates the clutter of anonymous inner classes. The builder maintains a map of names to components using the components property, which is an instance of this class:
class ComponentContainer {
def components
ComponentContainer() {
this.components = new HashMap()
}
public Object getProperty(String name) {
return components.get(name)
}
public Object get(name) {
return components.get(name)
}
public void put(name,value) {
components.put(name, value)
}
}
With this builder in place I was then able to rewrite Thinwire’s Hello World example like this:
public class Main {
public static void main(String[] args) {
def builder = new ThinwireBuilder()
builder.dialog(type: Dialog, label: "Hello World, ThinWire Style!", bounds: [25, 25, 215, 120]) {
label(type: Label, label: "Hello, what is your name?", bounds: [5, 5, 200, 25])
input(type: TextField, bounds: [5, 35, 200, 25])
button(type: Button, label: "Ok", bounds: [55, 65, 100, 25 ], click: {
MessageBox.confirm("Hello " + input.getText() + "!");
dialog.setVisible(false)
} )
}
def dialog = builder.components.dialog
dialog.setVisible(true);
}
}
Note that the closure that handles the click event references the input and dialog properties even though they have not been explicitly defined. That’s accomplished in the builder by setting the closure’s delegate to components. Quite slick!
The Main program is then specified as an init-param of the Thinwire Servlet:
<servlet>
<servlet-name>thinwire</servlet-name>
<servlet-class>thinwire.render.web.WebServlet</servlet-class>
<init-param>
<param-name>mainClass</param-name>
<param-value>net.chrisrichardson.thinwire-example.Main</param-value>
</init-param>
<//servlet>
I also had to add groovy support to the maven project, which is accomplished by using the maven groovy plugin:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
With that in place I could then type ‘mvn clean package cargo:start’ to run my Thinwire web application. This is what it looks like in the browser:

Next time, I’ll write about the Thinwire front-end for Project Track
– Chris Richardson
Tags: Groovy