Adding charts to Spring web applications with JFreeChart
I’m currently in the process of adding some reports based on data stored in a database. With annotated configuration in Spring 2.5 and the flexibility of JFreeChart, I was surprised at what can be achieved with such little effort.
The new features added to spring-mvc provide a nice way to avoid having to manage large configuration files and meant that I could concentrate on writing the code for my charts. I’ve added a quick example after the jump
The intention of my first chart was to read data from our database and present the information in a bar chart which will be available on an internal site.
As I was just adding to an existing Spring web application, all I had to do was configure a new spring-mvc DispatcherServlet in web.xml
<servlet>
<servlet-name>chart</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>chart</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
The ‘chart’ servlet now needs a corresponding chart-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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<context:annotation-config />
<context:component-scan base-package="uk.nominet.demo" />
</beans>
This configuration just instructs spring to search the classpath for all classes that are annotated with @Controller. Normally there would also be a ViewResolver configured too (for rendering the JSP, Freemarker, etc) but in this case the images will be streaming directly from the controller.
Now all that is left to do is write the code.
/** * Example Spring Controller for creating JFreeChart charts */ @Controller public class DemoChartController { @RequestMapping("/demo-chart.png") public void renderChart(String variation, OutputStream stream) throws Exception { boolean rotate = "rotate".equals(variation); // add ?variation=rotate to the URL to rotate the chart JFreeChart chart = generateChart(rotate); ChartUtilities.writeChartAsPNG(stream, chart, 750, 400); } private JFreeChart generateChart(boolean rotate){ DefaultCategoryDataset data = getDataset(); return ChartFactory.createBarChart( "example graph", // title "x-axis", // x-axis label "y-axis", // y-axis label data, rotate ? PlotOrientation.HORIZONTAL : PlotOrientation.VERTICAL, true, // legend displayed true, // tooltips displayed false ); // no URLs*/ } }
The above code handles the request and creates the chart. The @RequestMapping(”/demo-chart.png”) tells the DispatcherServlet what URL we are interested in whilst Spring is able to work out what to do with the parameters in the renderChart() method - pretty clever.
private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd MMM"); private DataSource dataSource; private DefaultCategoryDataset getDataset(){ DefaultCategoryDataset dataset = new DefaultCategoryDataset(); // Run the SQL query and add the results to the JFreeChart dataset for (Object o : getQueryResults()){ Map result = (Map) o; Number count = (Number) result.get("count"); String category = (String) result.get("categoryName"); String day = dateFormatter.format((Timestamp) result.get("day")); dataset.addValue(count, category, day); } return dataset; } private List getQueryResults(){ // Use Spring's JdbcTemplate to run the SQL query // Also see JFreeChart's JDBCCategoryDataset return (new JdbcTemplate(dataSource)).queryForList( "select count(id) count, categoryName, trunc(mydate) as day\n" + "from myTable\n" + "where mydate > sysdate - 14\n" + "group by categoryName, trunc(mydate)" ); } @Autowired public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; }
This last bit of code just executes the SQL query and populates a DataSet to use in the chart. There are of course better ways of doing this. The @Autowired dataSource is set from the main spring context in our web application. If you are creating an application from scratch you could add the dataSource definition to the chart-servlet.xml
Making the charts look good requires a little more effort and it’s best to create custom chart factories. Here I’ve used the same data but create a stacked bar chart which makes the weekends stand out…
Here is the basic factory method for this chart…
private static JFreeChart createChart(DefaultCategoryDataset dataset){ ValueAxis valueAxis = new NumberAxis("Value"); CategoryAxis categoryAxis = new CategoryAxis("Day"); categoryAxis.setLowerMargin(0.01d); // indent first bar categoryAxis.setUpperMargin(0.01d); // indent last bar categoryAxis.setCategoryMargin(0.1d); // space between categories BarRenderer renderer = new StackedBarRenderer(); renderer.setItemMargin(0d); // remove space between items CategoryPlot categoryPlot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); // change the colours of the bars GradientPaint gradient1 = new GradientPaint( 0.0f, 0.0f, new Color(51, 104, 155, 196), 0.0f, 0.0f, new Color(51, 104, 155, 64) ); GradientPaint gradient2 = new GradientPaint( 0.0f, 0.0f, new Color(0, 140, 208, 196), 0.0f, 0.0f, new Color(0, 140, 208, 64) ); categoryPlot.getRenderer(0).setSeriesPaint(0, gradient1); categoryPlot.getRenderer(0).setSeriesPaint(1, gradient2); // bring it all together return new JFreeChart("Bar Chart Demo", new Font("Helvetica", Font.BOLD, 12), categoryPlot, false); }
More about annotated controllers
More examples on JFreeChart. I used one of the ChartFactory examples in the code above.



August 29th, 2008 at 2:35 pm
For what it’s worth: Google has some nice charting to offer too:
http://code.google.com/apis/chart/
September 1st, 2008 at 9:30 am
I will probably try the Google chart API too to see which is simpler to use. It looks fantastic for ad-hoc charts but it does require you hand over your data.
Some of the charts we produce may be confidential so I’ll be using Eastwood (Jfreechart wrapper) that clones the Google API.
http://www.jfree.org/eastwood/
September 6th, 2008 at 6:06 pm
Hi!
Very very good solution… I like it! But I have a question..
Is the file PNG stored somewhere?
Is the file deleted after the user close the browser?
thanks..
Bye
September 8th, 2008 at 9:59 am
Andrew,
The PNG images are created for each request. They are not stored but are written straight to the http stream.
Just by having an OutputStream as a parameter in the renderChart() method, the Spring DispatcherServlet knows that we want the ServletResponse stream.
In a completely standard J2EE servlet, we would have used ServletResponse.getOutputStream().
The Javadoc for @RequestMapping lists all of the supported parameter types - http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/bind/annotation/RequestMapping.html
September 25th, 2008 at 4:17 pm
Hi Tom,
The tooltip is not working…
Is because the images are written directly in the http stream?
Thanks!
Bye
January 5th, 2009 at 12:13 am
How to create hyperlinks to the BARS in BARCHART and PIECHARTS?
May 22nd, 2009 at 7:41 am
Hi! I need a javascript method for updating charts. I tried to change image.src param, but controller’s method wasn’t called the second time. Do you have any advices for me? Thanks!
June 5th, 2009 at 10:24 am
Great sample!!
September 29th, 2009 at 6:50 am
The article is superb,how to add a horizantal scroll bar for the bar chart generated
June 1st, 2010 at 10:21 am
how about the JSP coding?
because it is controller…it should be via ajax?
if yes, how can I display the image?
really can’t understand.. please assist me on how to display the chart in JSP..