Monday, November 12, 2007

Simple web services using a Groovy MultiActionController in Spring

There have been many times I have wanted to create a simple XML API on top of my Spring applications. One technique I find myself using a lot is to create a scripted MultiActionController that uses the Groovy MarkupBuilder to render XML. (requires Spring 2.0 and above)

So as an example, Lets assume we have a Spring application created that is for an online store. (Download link for source code is below) We want to provide an xml representation of the products we offer from the following url:

http://localhost:8080/store/api/products

This will provide an xml document that looks like the following:
<products>
<product id="1" name="Product 1" price="2.5"/>
<product id="2" name="Product 2" price="3.5"/>
<product id="3" name="Product 3" price="4.5"/>
</products>

So the first step to creating our xml api is to setup a dispatcher servlet. We will add the following servlet definition and mapping to the web.xml.
<servlet>
<servlet-name>api</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>api</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>

The next step is to create the dispatchers bean definition xml. This will be a file named api-servlet.xml that will be located in the WEB-INF folder of your application. The following are the contents of the api-servlet.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/*">apiController</prop>
</props>
</property>
</bean>

<lang:groovy id="apiController" script-source="/WEB-INF/groovy/APIController.groovy">
<lang:property name="productService" ref="productService" />
</lang:groovy>

</beans>

Next step is to create the scripted controller. This will be called APIController.groovy and located in the WEB-INF/groovy folder.


import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.springframework.web.servlet.mvc.multiaction.MultiActionController
import groovy.xml.MarkupBuilder

class APIController extends MultiActionController {

def productService

void products(HttpServletRequest req, HttpServletResponse res) {
def products = productService.findAllProducts()
res.contentType = "text/xml"
def markupBuilder = new MarkupBuilder(res.writer)
markupBuilder.products {
for (p in products) {
product(id:p.id, name:p.name, price:p.price)
}
}
}

}



Thats it.. We can now deploy and access our web service. Additional methods could now be added to the APIController specified above to expose other data elements from the online store. One proposed method might be a product method that loads a particular product by id.

I have included a link to all the source code in a completed deployable web app in a maven project. It also takes advantage of the maven cargo plugin to support running of the application from within a container. All you have to do is have maven2 installed and run "mvn clean package cargo:start". Be patient it might take a while to fetch Tomcat the first time it runs.

Sample Project Structure:
.
|-- pom.xml
`-- src
`-- main
|-- java
| `-- com
| `-- acme
| `-- store
| |-- domain
| | `-- Product.java
| `-- service
| |-- DefaultProductService.java
| `-- ProductService.java
`-- webapp
|-- WEB-INF
| |-- api-servlet.xml
| |-- applicationContext.xml
| |-- groovy
| | `-- APIController.groovy
| `-- web.xml
`-- index.jsp


Dowload Sample Project Source Code

Thursday, October 11, 2007

Creating a Groovy Builder for Graphs using Prefuse

Groovy's BuilderSupport provides the capability to create a convenient syntax for building different types of structures. Some of the most commonly used examples may be for building XML documents or Swing user interfaces. In this post I am going to create a simple builder that creates a hierarchy of nodes that will be displayed visually using the Prefuse visualization library in a swing application.

A good approach to defining a convenient syntax is to start with a test driven approach and start coding a script that will use the builder. This example is only going to define the simplest of graphs with prefuse. Prefuse is a very powerful visualization library and there is so much more that could be added.

Below is the groovy code for working with the PrefuseBuilder:
  // create the prefuse graph
def prefuse = new PrefuseBuilder()
def graph = prefuse.graph {
node("Grand Parent") {
node("Parent") {
node("Child 1")
node("Child 2")
}
}
}
// create a swing app using the Groovy SwingBuilder to display the graph
def swing = new groovy.swing.SwingBuilder()
def frame = swing.frame(
title:'PrefuseBuilder Test', defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE) {
widget(graph)
}
frame.show()


The result of this graph will be the a swing application that looks like the following:



To simplify this example I am going to code the builder in Groovy, but this could also be implemented in a regular java class if preferred.

Below is the completed PrefuseBuilder class:

import groovy.util.BuilderSupport
import prefuse.Constants
import prefuse.Display
import prefuse.Visualization
import prefuse.action.ActionList
import prefuse.action.RepaintAction
import prefuse.action.assignment.ColorAction
import prefuse.action.assignment.DataColorAction
import prefuse.action.layout.graph.ForceDirectedLayout
import prefuse.activity.Activity
import prefuse.controls.DragControl
import prefuse.controls.PanControl
import prefuse.controls.ZoomControl
import prefuse.data.Graph
import prefuse.data.Node
import prefuse.render.DefaultRendererFactory
import prefuse.render.LabelRenderer
import prefuse.util.ColorLib
import prefuse.visual.VisualItem

class PrefuseBuilder extends BuilderSupport {

def graph
def visualization

void setParent(Object parent, Object child) {
if (parent instanceof Node && child instanceof Node) {
graph.addEdge(parent, child)
}
}

Object createNode(Object name) {
return createNode(name, null, null)
}

Object createNode(Object name, Object value) {
return createNode(name, null, value)
}

Object createNode(Object name, Map attributes) {
return createNode(name, attributes, null)
}

Object createNode(Object name, Map attributes, Object value) {
def node = null
if (name == 'node') {
node = graph.addNode()
node.setString("name", value)
visualization.run("color")
}
if (name == 'graph') {
graph = new Graph()
graph.addColumn("name", String.class)
visualization = new Visualization()
visualization.add("graph", graph)
def labelRenderer = new LabelRenderer("name")
labelRenderer.setRoundedCorner(8, 8)
visualization.setRendererFactory(new DefaultRendererFactory(labelRenderer))
def fill = new ColorAction("graph.nodes", VisualItem.FILLCOLOR, ColorLib.rgb(190,190,255))
def text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0))
def edges = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.gray(200))
def color = new ActionList()
color.add(fill)
color.add(text)
color.add(edges)
def layout = new ActionList(Activity.INFINITY)
layout.add(new ForceDirectedLayout("graph", true))
layout.add(new RepaintAction())
visualization.putAction("color", color)
visualization.putAction("layout", layout)
def display = new Display(visualization)
display.setSize(720, 500)
display.addControlListener(new DragControl())
display.addControlListener(new PanControl())
display.addControlListener(new ZoomControl())
display.setHighQuality(true)
visualization.run("color")
visualization.run("layout")
node = display
}
return node
}

}


That's all the code required to create this simple PrefuseBuilder. I am pretty new to Prefuse so I am not 100% sure that I am using the library correctly and I pretty much came up with the code for this from reverse engineering the Prefuse demos. The goal of this post was to present how easily builders can be created and to provide an example of the powerful capabilities they provide.