December 25th, 2009Ext JS Integration with Spring
Problem
Create Ext JS grid able to read data in XML format. Provide XML data from Spring controller. Bind it all together.
To demonstrate an integration between Ext JS and Spring frameworks I am going to use one of Ext JS grid samples – Progress Bar Pager. This grid displays Dow Jones components using Ext.data.ArrayReader which is one of the three, built in Ext.data.DataReader implementation. Since data will be served from a server, an ArrayReader has to be replaced with a XMLReader. Ext JS provides a helper class, XMLStore, which is automatically configured with an XMLReader, so I am going to use it.
DJIA Grid
Ext.onReady(function() {
// example of custom renderer function
function change(val) {
if (val > 0) {
return '<span style="color:green;">' + val + '</span>';
} else if (val < 0) {
return '<span style="color:red;">' + val + '</span>';
}
return val;
}
// example of custom renderer function
function pctChange(val) {
if (val > 0) {
return '<span style="color:green;">' + val + '%</span>';
} else if (val < 0) {
return '<span style="color:red;">' + val + '%</span>';
}
return val;
}
// create the data store
var xmlStockStore = new Ext.data.XmlStore({
autoDestroy : true,
url : '/zone/rest/djia',
record : 'component',
idPath : 'symbol',
totalProperty : 'totalRecords',
fields : [
{name : 'name'},
{name : 'price', type : 'float'},
{name : 'valChange', type : 'float'},
{name : 'pctChange', type : 'float'},
{name : 'lastUpdate', type : 'date', dateFormat : 'Y-m-d H:i:s.u T'}
]
});
// create the Grid
var grid = new Ext.grid.GridPanel({
store : xmlStockStore,
columns : [
{id : 'company', header : "Company", width : 160, sortable : true, dataIndex : 'name'},
{header : "Price", width : 75, sortable : true, renderer : 'usMoney', dataIndex : 'price'},
{header : "Change", width : 75, sortable : true, renderer : change, dataIndex : 'valChange'},
{header : "% Change", width : 75, sortable : true, renderer : pctChange, dataIndex : 'pctChange'},
{header : "Last Updated", width : 95, sortable : true, renderer : Ext.util.Format.dateRenderer('d-m-Y H:i'), dataIndex : 'lastUpdate'}
],
stripeRows : true,
autoExpandColumn : 'company',
height : 320,
width : 545,
frame : true,
title : 'DJIA',
bbar : new Ext.PagingToolbar({
pageSize : 10,
store : xmlStockStore,
displayInfo : true,
displayMsg : 'Displaying item {0} - {1} of {2}',
emptyMsg : "No item to display",
plugins: new Ext.ux.ProgressBarPager()
})
});
grid.render('grid-example');
xmlStockStore.load({params : {start : 0, limit : 10}});
});
The grid above is configured to consume an xml of the form:
<?xml version="1.0" encoding="UTF-8"?>
<djia>
<totalRecords>30</totalRecords>
<components>
<component>
<symbol>MMM</symbol>
<name>3M Co </name>
<price>60.46</price>
<valChange>0.12</valChange>
<pctChange>0.2</pctChange>
<lastUpdate>2009-12-24 15:00:00.0 CET</lastUpdate>
</component>
</components>
</djia>
So the last thing to do is to configure Spring to deliver an XML in a required form.
Thanks to Spring 3.0 REST support and ContentNegotiatingViewResolver providing an XML content is an extremally easy task. First, Spring configuration:
web.xml
<servlet> <servlet-name>SpringDispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value/> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringDispatcher</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>
applicationContext.xml
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order="1">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
</map>
</property>
<property name="defaultViews">
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<bean class="org.springframework.oxm.xstream.XStreamMarshaller"
p:autodetectAnnotations="true"
/>
</property>
</bean>
</property>
</bean>
As seen above, I am going to use an XStream library to serialize objects to XML. XStream is easy configurable with annotations, so the single DJIA component:
Component
import java.util.Date;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("component")
public class Component {
private String symbol;
private String name;
private Double price;
@XStreamAlias("valChange")
private Double valueChange;
@XStreamAlias("pctChange")
private Double percentChange;
private Date lastUpdate;
public Component () {
super ();
}
public Component(String symbol, String name, Double price, Double valueChange, Double percentChange, Date lastUpdate) {
super();
this.symbol = symbol;
this.name = name;
this.price = price;
this.valueChange = valueChange;
this.percentChange = percentChange;
this.lastUpdate = lastUpdate;
}
public String getSymbol() {
return symbol;
}
public String getName() {
return name;
}
public Double getPrice() {
return price;
}
public Double getValueChange() {
return valueChange;
}
public Double getPercentChange() {
return percentChange;
}
public Date getLastUpdate() {
return lastUpdate;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(Double price) {
this.price = price;
}
public void setValueChange(Double valueChange) {
this.valueChange = valueChange;
}
public void setPercentChange(Double percentChange) {
this.percentChange = percentChange;
}
public void setLastUpdate(Date lastUpdate) {
this.lastUpdate = lastUpdate;
}
}
and all of the components:
IndexDTO
import java.util.LinkedList;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.zone.domain.model.Component;
@XStreamAlias("djia")
public class IndexDTO {
private int totalRecords;
private LinkedList <Component> components;
public void setTotalRecords(int totalRecords) {
this.totalRecords = totalRecords;
}
public void setComponents(LinkedList <Component> components) {
this.components = components;
}
public int getTotalRecords() {
return totalRecords;
}
public LinkedList <Component> getComponents() {
return components;
}
}
IndexDTO is a helper class, which will be eventually marshalled and sent to browser, totalRecords variable is optional, but grid PagingToolbar will not work without it.
Finally, the controller:
IndexController
import java.util.LinkedList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.zone.domain.model.Component;
import com.zone.infrastructure.repository.DJIARepository;
import com.zone.web.grid.IndexDTO;
@Controller
public class IndexController {
@Autowired
DJIARepository djiaRepository;
@RequestMapping(value = "/djia")
public void getComponents(@RequestParam("start") int start, @RequestParam("limit") int limit, Model model) {
List <Component> components= (List<Component>) djiaRepository.findAll();
IndexDTO indexDTO = new IndexDTO ();
indexDTO.setTotalRecords(components.size());
indexDTO.setComponents(new LinkedList<Component> (components.subList(start, start+limit > components.size() ? components.size() : start+limit)));
model.addAttribute("indexDTO", indexDTO);
}
}
DJIARepository is a class holding all DJIA components.
Conclusion
It is actuall all, what is necessary, to have an Ext JS grid populated with data coming from Java based back end. Of course, all hard work is done by Spring and Xstream with some configuration and programming left to the application developer.

