Visualize Your Oracle Database Data with JFreeChart

DBA: Administration

By Dustin Marx and Michael G. Martin

Use JFreeChart to easily plot your Oracle database data in a wide selection of high-quality chart types.

Updated October 2007

Charts, figures, and graphs enable large sets of data to be more quickly understood and analyzed. It is often said that "a picture is worth a thousand words" because a picture can convey significant overview and comparative information about data that is much more difficult to glean from the textual data itself. JFreeChart is an open source (and free) Java-based library that allows for easy creation of multiple types of high-quality data-centric charts. This powerful library provides a highly approachable API that enables developers new to JFreeChart to rapidly create elegant charts representing their data.

In this article, we will provide an example of this process using HR Schema data from Oracle Database 10g Express Edition (XE). Full source code for the example can be downloaded here.

Introduction to JFreeChart

The JFreeChart project started back in 2000. The most recent release of this library, version 1.0.6, was released in June 2007 and is the version this article uses.

JFreeChart supports generation of a wide variety of chart types. They range from common types such as pie charts, bar charts, area charts, line charts, histograms, and Gantt charts to more-specific and less frequently used types such as Candlestick, Wind, and Wafer Map charts. One of the quickest ways to determine the types of charts JFreeChart supports out-of-the-box is to examine the Javadoc API documentation for JFreeChart's ChartFactory class.

There are several development environments in which JFreeChart can be effectively used to render data-oriented charts, including the following:

  • Generated files in Portable Network Graphics (PNG) or JPEG format
  • Java SE Swing-based applications, including applets
  • Java EE servlets and JavaServer Pages (JSP)
  • Integration with iText to create PDFs
  • Integration with Batik to create SVG format

The Data to Be Charted

The data to be graphically represented in this article's charts comes from the HR schema provided with Oracle Database XE. Figure 1 shows the tables present in the HR schema as shown in the SQL*Plus editor that comes with Oracle Database XE.

Figure 1: HR Schema tables displayed in SQL command-line tool

Oracle Database XE also provides a highly useful, easy-to-use Web-based administrative tool that is built on Oracle Application Express. Figure 2 demonstrates this tool and again shows some of the objects (tables in this case) in the HR schema.

Figure 2: HR Schema tables in Oracle Application Express

Oracle Database XE offers several advantages for developers and is a natural fit with JFreeChart. Oracle has made Oracle Database XE available at no cost for development and production. Besides its no-cost advantage, other advantages of this database product include a smaller footprint, easy installation and administration, and the highly useful integrated Web administration tool. Its disadvantages affect primarily large enterprise users, because the drawbacks relate to storage space, memory size, and a smaller feature set than that of traditional Oracle database products. For development and prototyping, and even for small production databases, Oracle Database XE can be exactly the right fit. This is especially true of businesses or projects with very limited budgets.

The Web-based administration tool, shown again in the snapshot in Figure 3, displays some of the most significant limitations of Oracle Database XE. Storage space for an instance of Oracle Database XE is limited to 5 GB (including system tablespace), so the 710 megabytes used so far is depicted in the tool in the screenshot as around 14 percent of that limit. Memory usage is limited to 1 gigabyte, so this instance's 314MB RAM usage is depicted as such. Logging into this administration tool as SYSTEM enables even-more-specific storage and memory information to be displayed on the administrative screens. The screen shot in Figure 3 also shows all of the submenu options the tool offers for managing the database.

Figure 3: Oracle Database XE tools and usage monitors

Having introduced Oracle Database XE and the HR schema that is the source of data for this article's chart generation, we will now return to JFreeChart itself.

Downloading and "Installing" JFreeChart

JFreeChart is available for download at www.jfree.org/jfreechart/. The single downloaded file contains several JAR files for JFreeChart. Once the downloaded file is uncompressed to the directory of your choice, the classpath of any application using JFreeChart needs to point at least to the jfreechart-1.0.5.jar and jcommon-1.0.9.jar files. You can handle this by adding these JAR files to your favorite IDE's project. For building and running outside of an IDE, Java SE 6 allows wildcards to be used for specifying JARs in classpaths, so the JARs in the JFreeChart lib directory can all be included with a single *.jar expression in the classpath. Other than uncompressing the distribution file and pointing your application's classpath at the appropriate JFreeChart JAR files, no significant installation effort is required.

Configuring Oracle JDeveloper for Example Code Dependencies

Figure 4 shows the dependent JAR files necessary to build and run most of the code examples in this article. The two JFreeChart JARs mentioned earlier are listed, along with JARs for iText (for PDF generation), Batik (for SVG generation), and Oracle JDBC (for Java access of the Oracle database). Even if you are not using JDeveloper, Figure 4 displays in a single location the dependencies you will need to place in your classpath to build and run most of the tools discussed in this article.

Figure 4: JARs to add to JDeveloper project (or otherwise place in classpath)

Generating JFreeChart Javadoc API Documentation

The JFreeChart compressed distribution file also includes a build.xml file that is especially useful for generating JFreeChart Javadoc-based documentation. To generate the JFreeChart documentation, run the "javadoc" target on the provided Ant build.xml file (run "ant javadoc" in the "ant" subdirectory). Assuming that you have Ant installed, this will generate a "javadoc" subdirectory under the directory in which the JFreeChart files were extracted and will place the generated Javadoc files in that directory. The Javadoc documentation for JFreeChart is also available online at the JFreeChart Web site.

A Brief Overview of JFreeChart's Key Classes and Interface

JFreeChart comes with many packages, classes, and interfaces, but you need to understand only a surprisingly small number of them to start using JFreeChart. This section briefly covers some of the key classes and interfaces that are fundamental to using JFreeChart and that are used in examples in this article.

The JFreeChart Class

One of the most important classes in the JFreeChart library is itself called JFreeChart. This class provides a representation of a Java 2D chart. Methods on this class enable developers to control various aspects of a generated chart and to create an Abstract Window Toolkit (AWT) BufferedImage representing the chart. You can create several simple types of charts by using JFreeChart directly, with very little extra effort, but JFreeChart provides access to delegates such as Plot that allow for more control over the rendered charts.

The ChartFactory Class

The ChartFactory class is used to create different types of charts. Each of the static methods of this class is named after the type of chart it produces, and each of these methods returns an instance of the generic JFreeChart class, regardless of the type of chart involved. Scanning the Javadoc documentation for this class can provide many insights and is one of the easiest ways to determine the basic chart types provided out-of-the-box by JFreeChart.

The ChartUtilities Class

Similar to the ChartFactory class, the ChartUtilities class has numerous static methods. Most of the methods this class provides handle conversion of a chart to an image format or to basic HTML image maps.

Dataset Interfaces and Implementation Classes

Examination of the ChartFactory chart creation methods indicates that each of these methods accepts some type of Dataset-extended interface (note Dataset in the class's name) as a parameter. The generated Javadoc documentation for the Dataset interface shows that it is extended by several subinterfaces and implemented by an even greater number of concrete classes.

The Dataset subinterfaces to use for various types of charts are typically obvious, because the chart type's name often appears in the interface name. For example, PieDataset is used for pie charts and XYDataset is used for many x,y-type charts. CategoryDataset is used for line and area charts, and other subinterfaces that extend the Dataset interface are used for other chart types.

Dataset subinterfaces are the mechanism for providing data to the ChartFactory class to be represented in a chart. A concrete implementation of the appropriate Dataset subinterface is created and populated and passed to ChartFactory's appropriate create***Chart method, where *** is the pertinent chart type.

The Dataset interface JFreeChart provides should not be confused with the Dataset interface provided by JDBC 4. The JFreeChart-provided Dataset interface does not have to be populated from the database or be related to the database in any way. Instead, the JFreeChart Dataset interface and its subinterfaces are designed for populating JFreeChart charts with data.

See JFreeChart in Action: Examples

Much of the remainder of this article is devoted to illustrating some of JFreeChart's features and how easy it is to begin using JFreeChart. This illustration of JFreeChart's usability and feature set is accomplished through a series of code examples with snapshots of the produced charts. As each of the examples is presented, pertinent points related to each specific example are presented, along with additional general JFreeChart concepts.

Example 1: Charting HR Schema Data Directly in a Pie Chart

Most of the examples in this article involve creating charts with JFreeChart, based on data stored in the HR schema provided with Oracle Database XE. Because we are charting data stored in the database, the JDBC-oriented Dataset classes are particularly convenient. This initial example shows how to chart the number of employees in each department in a pie chart.

Listing 1 demonstrates how easy it is to create a chart based on data stored in the database. The databaseAccess.getOracleDbConnection() method, not shown here, is a simple method that provides an Oracle database connection (returns a java.sql.Connection).

Listing 1: Creating a Pie Chart with JFreeChart's JDBCPieDataset


 
 
 /**
 * Create pie chart representing percentage of employees in each department
 * that has at least one employee.
 * 
 * @return Pie chart; null if no image created.
 */
 public JFreeChart createNumberEmpsPerDeptPieChart()
 {
 JFreeChart pieChart = null;
 
 final String QUERY_NUMBER_EMPLOYEES_PER_DEPARTMENT =
 "SELECT departments.department_name, count(*) AS num_employees " +
 "FROM departments, employees " +
 "WHERE employees.department_id = departments.department_id " +
 "GROUP BY departments.department_name";
 
 final String TITLE_EMPS_PER_DEPT = “Employees Per Department”;
 
 try
 {
 PieDataset pieDataset =
 new JDBCPieDataset( databaseAccess.getOracleDbConnection(),
 QUERY_NUMBER_EMPLOYEES_PER_DEPARTMENT );
 
 pieChart =
 ChartFactory.createPieChart( TITLE_EMPS_PER_DEPT, // chart title
 pieDataset,
 true, // legend displayed
 true, // tooltips displayed
 false ); // no URLs
 
 }
 catch (SQLException sqlEx) // checked exception
 {
 System.err.println("Error trying to acquire JDBCPieDataset.");
 System.err.println("Error Code: " + sqlEx.getErrorCode());
 System.err.println("SQLSTATE: " + sqlEx.getSQLState());
 sqlEx.printStackTrace();
 }
 
 return pieChart;
 }
 
 

As Listing 1 demonstrates, only two significant Java code statements are required to produce a pie chart from the database. The appropriate Dataset is created and then passed to the ChartFactory's createPieChart method, which returns the JFreeChart containing the generated pie chart. A JDBCPieDataset was constructed and passed a database connection and a database SQL query string. Because that concrete class implements the PieDataset interface, we could then pass that, via the interface, to the createPieChart method. The catch block for SQLException in this code listing was necessary because the JDBCPieDataset constructor throws the checked exception SQLException.

Once we have a chart created and in the form of an instance of the JFreeChart class, there are several things we can do with the chart. One of the easiest things to do with a created chart is to write it to the file system as an image file. The code in Listing 2 illustrates use of ChartUtilities to write our newly created JFreeChart to the file system as a PNG formatted image.

Listing 2: Writing Chart to PNG File


 
 /**
 * Write .png file based on provided JFreeChart.
 * 
 * @param aChart JFreeChart.
 * @param aFileName Name of file to which JFreeChart will be written..
 * @param aWidth Width of image.
 * @param aHeight Height of image.
 */
 public void writePngBasedOnChart( JFreeChart aChart,
 String aFileName,
 int aWidth,
 int aHeight )
 {
 try
 {
 ChartUtilities.writeChartAsPNG( new FileOutputStream(aFileName),
 aChart,
 aWidth, aHeight);
 }
 catch (IOException ioEx)
 {
 System.err.println("Error writing PNG file " + aFileName);
 }
 }
 

This code listing has only one significant Java code statement for writing a JFreeChart chart out as a PNG file. That single statement is a call to the ChartUtilities.writeChartAsPNG method, which is passed the newly created JFreeChart chart from Listing 1, along with a String indicating the name of the file to which we want the image to be written and the width and height of the image. Note that this generic method can accept any JFreeChart chart and can thus be used for all chart types. In this case, we constructed a pie chart, shown in Figure 5. The chart quickly relays that the vast majority of the employees work in either Shipping or Sales.

Figure 5: PNG Format Pie chart created from DB query

Not counting "infrastructure" lines of code for exception handling, acquiring a database connection, and returning classes, only three significant Java code statements are required to create the chart in Figure 5 directly from the database. The three statements correspond to the three steps generally required for generating basic charts from the database with JFreeChart:

  • Instantiate the appropriate JDBC-oriented Dataset with a valid database connection and appropriate database SQL query String.
  • Pass the JDBC-oriented Dataset from the previous step to the appropriate ChartFactory.create***Chart method for the desired type of chart.
  • Render the newly created JFreeChart in the appropriate format (PNG file in the first example).

Throughout the remainder of this article, other approaches for rendering generated charts are presented, along with examples of other types of charts.

Example 2: Charting HR Schema Data Indirectly in a 3-D Pie Chart

In the initial example, a pie chart was constructed with a dataset retrieved directly using a JDBC-oriented Dataset. The specific implementation was constructed using a provided database connection and SQL query statement. However, there are situations in which using a JDBC-oriented dataset may not be possible or desirable. For example, JFreeChart provides JDBC-oriented Dataset implementations only for the PieDataset, CategoryDataset, and XYDataset subinterfaces. There may also be situations in which the data is not easy to extract from the database in appropriate format for JFreeChart and some manipulation of the data must first occur in Java code. Finally, it may not be desirable to couple chart generation so closely with a database query. In either of these cases, a Dataset that is not JDBC-oriented can be used instead. Listing 3 demonstrates how this can be implemented for a 3-D pie chart.

Listing 3: Manually Constructing a Dataset and Rendering a JPG Chart


 
 JFreeChart pieChart3D = null;       
final DefaultPieDataset pieDataset = new DefaultPieDataset();     
/* Query used by getNumberEmployeesByLocation() method: 
SELECT locations.city, count(*) AS num_employees     
FROM departments, employees, locations          
WHERE employees.department_id = departments.department_id   
AND departments.location_id = locations.location_id      
GROUP BY locations.city     
*/      
Map<String, Integer> empsAtEachLocation=  
databaseAccess.getNumberEmployeesByLocation();    
for ( Map.Entry numEmpsAtLoc : empsAtEachLocation.entrySet() )  
{      
pieDataset.setValue(        
numEmpsAtLoc.getKey().toString(),   
Integer.parseInt(numEmpsAtLoc.getValue().toString()) );    
}       
pieChart3D = ChartFactory.createPieChart3D( TITLE_EMPS_PER_LOC, 
pieDataset,                     
true,         
true,           
false );    
final String fileName = "EmpsPerLocPieChart3D.jpg";   
try     
{       
ChartUtilities.writeChartAsJPEG( new FileOutputStream(fileName),    
aChart,  

aWidth, aHeight);     
}  
catch (IOException ioEx)     
{       
System.err.println( "Error writing JPG file " + fileName);   
}
 

The code in Listing 3 is slightly more verbose than that required in the first example, because it does not take advantage of the JDBC Dataset for pie charts and instead manually copies Map entries from its database accessor (databaseAccess.getNumberEmployeesByLocation) into a DefaultPieDataset that is passed to the createPieChart3D method. Although this code is longer than that required for the first example, it has the advantages of not needing to deal with SQLException and being less directly coupled to the database query.

This example also produces a JPEG (.jpg) file rather than a PNG (.png) file such as the image generated in the first example. Both are image file formats, and both examples show how easy it is to write a chart out to either image format. The actual JPEG image created as a result of running the code in Listing 3 is shown in Figure 6.

Figure 6: 3-D pie chart created in JPEG format from a database

One of the things that strikes you in this graphical representation of the data is that most of the employees work in South San Francisco, Oxford, or Seattle.

Because using the JDBC-oriented datasets removes the need for converting from a Java collection or ResultSet to the Dataset format JFreeChart expects, we will use that approach when possible throughout the remainder of this article to maintain simplicity. However, as stated earlier, there are situations in which this approach is not possible or not desirable because of the strong coupling of database access to the chart generation. Similarly, although it is necessary to catch the checked exception SQLException when you are using the JDBC-oriented Datasets, we will omit most of the exception handling code in future code listings in this article.

Example 3: Rendering a Bar Chart in Swing

This example demonstrates using JFreeChart to produce a bar chart, and it also introduces the idea of displaying a JFreeChart-generated chart in Swing. Listing 4 displays the most significant code for generating the chart and for displaying the generated chart in Swing.

Listing 4: Generating a Bar Chart from Java Swing


 
 
 /**
 * Create Bar Chart showing salary of each employee.
 * 
 * @param aOrientation Horizontal or Vertical orientation of bar chart.
 * @return Bar Chart.
 */
 public JFreeChart createSalaryPerFinanceEmployeeBarChart(
 PlotOrientation aOrientation)
 {
 JFreeChart barChart = null;
 
 try
 {
 final String QUERY_SALARY_PER_FINANCE_EMPLOYEE =
 "SELECT first_name || ' ' || last_name AS Name, salary " +
 "FROM employees " +
 "WHERE department_id = 100";
 
 final CategoryDataset barDataset =
 new JDBCCategoryDataset( databaseAccess.getOracleDbConnection(),
 QUERY_SALARY_PER_FINANCE_EMPLOYEE );
 
 barChart =
 ChartFactory.createBarChart( TITLE_SALARY_PER_FINANCE_EMP, // title
 LABEL_EMPLOYEES_FINANCE, // x-axis label
 LABEL_SALARIES, // y-axis label
 barDataset,
 aOrientation,
 true, // legend displayed
 true, // tooltips displayed
 false ); // no URLs
 
 }
 catch (SQLException sqlEx)
 {
 // . . . exception handling goes here . . .
 } 
 
 return barChart;
 }
 
 . . .
 
 JFrame frame = new JFrame(aTitle);
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
 JFreeChart barChart =
 createSalaryPerFinanceEmployeeBarChart(
 PlotOrientation.VERTICAL);
 
 BufferedImage image = barChart.createBufferedImage(750,450);
 JLabel label = new JLabel();
 label.setIcon(new ImageIcon(image));
 frame.getContentPane().add(label);
 
 frame.pack();
 frame.setVisible(true);
 
 

In place of hard-coded strings, this code example uses String constants, which are easily identified by their naming convention of all uppercase letters with words separated by underscores.

The two previous pie chart generation examples used a PieDataset. This bar chart generation example uses a CategoryDataset instead. Unlike with a pie chart, orientation is an issue with bar charts and an orientation of vertical or horizontal can be specified. This example specifies a vertical plot orientation.

Toward the bottom of the code listing, the example code demonstrates how to retrieve the chart as a BufferedImage that can easily be applied to a JLabel as an icon. Figure 7 shows a snapped picture of this simple Swing Graphical User Interface (GUI) with the JFreeChart-generated bar chart.

Figure 7: Generated bar chart in Swing application

Example 4: Rendering a Chart in a Java Servlet

This example demonstrates rendering a JFreeChart from within a servlet container. As shown in the previous example, JFreeChart can render any chart to a BufferedImage. The following code listings leverage this capability and add a sample company logo to the JFreeChart-generated image. The final image and servlet output stream are passed to JFreeChart's ChartUtilities class for writing.

Listing 5: Rendering a Chart from a Java Servlet


 
 
 /**
 * Processes requests for both HTTP 
 GET and 
 POST methods.
 * @param request servlet request
 * @throws javax.servlet.ServletException
 * @throws java.io.IOException
 * @param response servlet response
 */
 protected void processRequest(HttpServletRequest request, 
 HttpServletResponse response)
 throws ServletException, IOException {
 
 ImageType imageType = ImageType.valueOf(request.getParameter("type"));
 int imageWidth = Integer.valueOf(request.getParameter("width"));
 int imageHeight = Integer.valueOf(request.getParameter("height"));
 float imageQuality = Float.valueOf(request.getParameter("quality"));
 
 if (imageType.equals(ImageType.PNG)) {
 response.setContentType("image/png");
 } else if (imageType.equals(ImageType.JPEG)) {
 response.setContentType("image/jpg");
 }
 
 HrJobsData data = getHrJobData();
 writeImage(imageType,data,response.getOutputStream(),imageWidth,imageHeight,
 quality);
 response.getOutputStream().close();
 }
 
 
 

The code in Listing 5 shows the data being provided by the getHrJobData() method. That method's implementation is not shown here, but it is simply a method that retrieves the data from the persistence source. Because this code is executed in a servlet container, a datasource should be used to take advantage of the servlet container's transaction support and the datasource's connection pooling. Use of a datasource also provides the database connection to our code in a vendor-independent manner.

The processRequest method is called from the servlet's doGet and doPost methods. The servlet expects the image type, width, height, and quality to be passed in. Currently, the servlet supports images of type PNG or JPEG represented by a simple enum. The data object is an XML object representing the data we want to chart. The servlet sets the correct content type based on the passed-in image type and then calls writeImage to create and send the image over the HTTP response output stream.

Listing 6: Customizing a Chart's Renderer


 
 
 private void writeImage(ImageType imageType,HrJobsData data,
 OutputStream outputStream, int width, int height, float quality)
 { 
 try
 {
 double[][] minWages = new double[1][data.getHrJob().size()];
 double[][] maxWages = new double[1][data.getHrJob().size()];
 String[] categories = new String[data.getHrJob().size()];
 
 int index = 0;
 for (HrJob hrJob : data.getHrJob())
 {
 minWages[0][index] = hrJob.getMinimumSalary();
 maxWages[0][index] = hrJob.getMaximumSalary();
 categories[index] = hrJob.getJobTitle();
 index++;
 }
 
 DefaultIntervalCategoryDataset dataset = new
 DefaultIntervalCategoryDataset(minWages,maxWages);
 dataset.setCategoryKeys(categories);
 
 JFreeChart chart = ChartFactory.createBarChart("Job Salary Ranges", 
 "Job Title", "Salary Range",
 dataset, PlotOrientation.HORIZONTAL, false, false, false);
 
 IntervalBarRenderer renderer = new IntervalBarRenderer();
 ((CategoryPlot)chart.getPlot()).setRenderer(renderer);
 
 BufferedImage img = chart.createBufferedImage(width, height);
 BufferedImage logo = getLogo();
 Graphics2D graphics2D = (Graphics2D) img.getGraphics();
 graphics2D.drawImage(logo, null,LOGO_OFFSET_X, LOGO_OFFSET_Y);
 graphics2D.dispose();
 
 if (imageType.equals(ImageType.JPEG))
 {
 ChartUtilities.writeBufferedImageAsJPEG(outputStream, quality, img);
 }
 else if (imageType.equals(imageType.PNG))
 {
 // PNG scales the quality from 0-9
 int pngQuality = (int) (quality * 9);
 ChartUtilities.writeBufferedImageAsPNG( outputStream, img,
 true, pngQuality);
 }
 }
 catch (IOException ex)
 {
 // . . . exception handling goes here . . .
 }
 }
 

The data we want to chart represents salary ranges for various positions. A JFreeChart bar chart with an IntervalBarRenderer provides a nice view for this set of data. The passed-in XML data is iterated over, and three arrays are filled with the minimum salary, maximum salary, and job title. Calling createBufferedImage on the generated chart provides a BufferedImage. A call to getLogo returns our company logo contained inside another BufferedImage. Using Java's 2-D graphics capabilities, the company logo is drawn onto the chart image at a small offset from the chart image origin.

The quality parameter allows fine control over the compression level. For a JPEG image, the quality ranges from 0.0 to 1.0. Lower values result in smaller files at the expense of image quality. For a PNG image, the quality ranges from 0 to 9. PNG is a lossless format, so the quality is applied to the compression level of the data without compromising quality. The quality of the resultant PNG image remains the same as that of the original.

A final call to ChartUtilities.writeBufferedImageAsPNG or ChartUtilities.writeBufferedImageAsJPEG makes it easy for the servlet to send the final image back to the browser.

To call the servlet, a standard HTML img tag can be used. Listing 7 shows a simple JSP page that uses an embedded img tag to call the servlet and display a rendered chart inside the Web page.

Listing 7: JSP Invoking Servlet to Render Chart


 
 <html> 
 <head> 
 <meta http-equiv="Content-Type" CONTENT="text/html;charset=utf-8"> 
 <title>Title Salary Ranges</title> 
 </head> 
 <body> 
 <h1 style="text-align:center">Job Salary Ranges</h1> 
 <img src="<%=request.getContextPath()%>/GetChart?type=PNG&width=800&height=400&quality=.9"></img> </body> </html>
 
 

The final result of the servlet's being called to return a JFreeChart image is shown in Figure 8.

Figure 8: Servlet-built interval bar chart in PNG Format

Example 5: Rendering a Chart in a JSP with Cewolf

Cewolf (Chart Enabling Web Object Framework) is built on JFreeChart and enables developers to access JFreeChart libraries from JavaServer Pages (JSP) via JSP custom tags. Like JFreeChart, Cewolf provides a simple API. Cewolf is available at no cost. For this example, we use Cewolf 0.9.3.

The image in Figure 9 is a screen shot of a vertical bar chart rendered with JFreeChart and Cewolf JSP tags. It is rendered in the Firefox Web browser and is hosted on Oracle Containers for J2EE 10 g. This image proves that it pays well to be an executive in the fictitious company represented in the HR data.

Figure 9: Stacked bar chart rendered in JSP with Cewolf JSP custom tags

With Cewolf and JSP, creating the chart shown in the figure above is simple. Here is the code from that JSP page:

Listing 8: JSP Page Using Cewolf Tags to Render Chart


 
<?xml version='1.0' encoding='windows-1252'?>   
xmlns:cewolf="etc/cewolf.tld"> 
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML"      
doctype-system="http://www.w3.org/TR/html4/loose.dtd"    
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/> 
<jsp:directive.page contentType="text/html;charset=windows-1252"/>  <jsp:useBean id="chartData"         
\       class="org.marx.hr.charting.HrDeptSalariesCategoryDataset"/>

<html>  
<head> 
<meta http-equiv="Content-Type"  
CONTENT="text/html;charset=utf-8"/>       
<title>Render JFreeChart in JSP with Cewolf JSP Tag Library</title>  
</head>  
<body>  
<cewolf:chart id="theChart" type="stackedverticalbar"        
title="Salaries For Each Department"       
xaxislabel="Departments" yaxislabel="Salaries">  
<cewolf:data>      
<cewolf:producer id="chartData" />  
</cewolf:data>    
</cewolf:chart>    
<cewolf:img chartid="theChart" renderer="/cewolf"    
alt="Salaries per Employee Along Department Lines"    
width="1200" height="800" />
</body> 
</html> 
</jsp:root>
 

The portions of the code specific to Cewolf include the lines between the opening and closing tags for the <cewolf:chart> element, the <cewolf:img> element, the line pointing to the Cewolf namespace to define the cewolf prefix for these elements, and the jsp:useBean line to include the Java class that actually produces the chart.

The majority of the work that went into producing this example lies in the Java class accessed by this JSP page via the jsp:useBean lines. That Java class implements several key Cewolf interfaces and returns a JFreeChart Dataset subinterface from its produceDataset(Map) method (required to implement Cewolf's DatasetProducer interface).

The code in a produceDataset(Map) method simply creates a concrete implementation of the appropriate JFreeChart Dataset interface, exactly as in the previous examples in this article. In this case, a DefaultCategoryDataset is instantiated, populated with data, and returned from the produceDataset(Map), because the JSP page expects something that implements the CategoryDataset interface to be provided for rendering the stacked vertical bar chart.

The Cewolf home page includes a tutorial that demonstrates how to modify the web.xml file so that the Cewolf servlet is invoked when appropriate. This tutorial also explains how to implement the DatasetProducer interface to provide the data to the Cewolf custom tags in the JSP page.

Example 6: Rendering a Chart in SVG with Apache Batik

Out-of-the-box and with no special effort required, JFreeChart provides for generation of PNG and JPEG images. Because charts are inherently composed of lines as well as shapes such as rectangles, circles, and other vector-ready shapes, a vector-based format such as SVG (Scalable Vector Graphics) seems to be a natural fit for charts. Although JFreeChart does not provide SVG rendering capability directly, it can be used in conjunction with Apache's Batik SVG Toolkit to produce SVG representations of its charts.

The Batik SVG Toolkit is a Java-based toolkit for manipulating SVG, and it is available free of charge. Batik 1.7 is used in this example. Several executable JAR files are included in the distribution. One of these files (batik.jar) is an executable JAR that runs an SVG handling and viewing tool called Squiggle (java –jar batik.jar).

In this example, we will first use Batik directly in our own Java code as a library and then we will use the executable JAR (batik.jar) to run Squiggle to view our generated SVG file. To build and run the Batik-related code in this example, you must include six JAR files in the classpath (or JDeveloper's or other IDE's project). These six JAR files (the six JAR files beginning with "Batik") are shown back in Figure 4.

For this example, the generated chart is a 3-D bar chart that shows shipping clerks' salaries. This is different from the previous bar chart example (Example 3), because it has a 3-D, rather than 2-D, appearance and is specified as a horizontal rather than vertical bar chart. The following two code listings, respectively, show how to generate this chart and how to write it out in SVG format.

Listing 9: Creating a 3-D Bar Chart


 
 /**
 * Create 3D bar chart representing salaries of shipping clerks.
 * 
 * @param aOrientation Horizontal or Vertical orientation.
 * @return 3D bar chart.
 */
 public JFreeChart createSalaryPerShippingClerkBarChart3D(
 PlotOrientation aOrientation)
 {
 JFreeChart barChart = null;
 
 try
 {
 final String QUERY_SALARY_PER_SHIPPING_CLERK =
 "SELECT first_name || ' ' || last_name AS name, salary " +
 "FROM employees " +
 "WHERE job_id = 'SH_CLERK'";
 final CategoryDataset barDataset =
 new JDBCCategoryDataset( databaseAccess.getOracleDbConnection(),
 QUERY_SALARY_PER_SHIPPING_CLERK );
 
 barChart =
 ChartFactory.createBarChart3D( TITLE_COUNTRIES_PER_REGION, // title
 LABEL_SHIPPING_CLERKS,
 LABEL_SALARIES,
 barDataset,
 aOrientation,
 true, // legend displayed
 true, // tooltips displayed
 false ); // no URLs
 }
 catch (SQLException sqlEx)
 {
 // exception handling code . . .
 }
 
 return barChart;
 }
 
 

The previous code listing demonstrates that creating a 3-D bar chart is essentially identical to creating a 2-D bar chart, with the single exception of the name of the method called on the ChartFactory class. The following code listing shows how this or any other generated JFreeChart can be written out to SVG format.

Listing 10: Rendering a JFreeChart Generated Chart in SVG Format


 
 /**
 * Write .svg file based on provided JFreeChart.
 * 
 * @param aChart Chart to be written out as SVG.
 * @param aFileName Name of file (without extension) to hold SVG XML.
 * @param aWidth Width of image.
 * @param aHeight Height of image.
 */
 public void writeSvgBasedOnChart( JFreeChart aChart,
 String aFileName,
 int aWidth,
 int aHeight )
 {
 final String fileExtension = ".svg";
 final String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
 final DOMImplementation dom =
 GenericDOMImplementation.getDOMImplementation();
 org.w3c.dom.Document document = dom.createDocument(svgNS, "svg", null);
 SVGGraphics2D svgGraphic = new SVGGraphics2D(document);
 aChart.draw( svgGraphic, new java.awt.Rectangle(aWidth, aHeight) );
 
 try
 {
 boolean useCSS = true; // we want to use CSS style attributes
 OutputStream outputStream =
 new FileOutputStream(aFileName + fileExtension);
 Writer output = new OutputStreamWriter(outputStream, "UTF-8");
 svgGraphic.stream(output, useCSS); 
 outputStream.flush();
 outputStream.close();
 }
 catch (IOException ioEx)
 {
 System.err.println("Problem encountered trying to write SVG file: ");
 ioEx.printStackTrace();
 }
 }
 

When the 3-D bar chart is passed to the method above for writing out SVG, it is written to whatever filename is provided. The generated SVG file will simply be a text file with an .svg extension and SVG-compliant XML content. The XML in that generated file can be viewed with any text editor, but it is more interesting to view the SVG as it is rendered as an image by a tool such as Batik, Inkscape, or almost any modern graphics file editor. In fact, you can view SVG by opening an .svg file directly in any recent version of the Firefox Web browser, because it comes with a built-in SVG viewer. The figure shown here is a snapshot of Batik's Squiggle rendering of the produced SVG file.

Figure 10: 3-D horizontal bar chart in SVG format (rendered by Batik Squiggle)

There are several advantages to having our chart in SVG format, one of the most obvious being the ability to zoom in on the image as much as we like without losing resolution, thanks to SVG vector graphics' scalability. Other advantages include the relative ease of editing the generated SVG with a tool such as Inkscape or even by hand, if necessary, to tweak the produced image.

Example 7: Rendering a Chart in PDF with iText

Charts are often useful for presentations to management, customers, shareholders, and other stakeholders. However, presenting charts via Java applications or as flat files is often not suitable for these types of audiences. It can be helpful to add the generated charts to a PDF for presentation to different audiences. There are several Java-to-PDF converters that are suitable for this purpose. Here we demonstrate a simple example of using one of these Java-to-PDF libraries, iText.

The iText Java-to-PDF library is available at no cost . The version used in our example is itext-2.0.2, a relatively small file to download. This downloaded compiled JAR is executable and can be run with the java –jar itext-2.0.2 command, or you can click on the JAR in Windows. Running this JAR brings up the "iText Toolbox" Human Machine Interface (HMI), which can be used to convert text or TIFF images to PDF and provides additional utility functionality. This example does not use this HMI but instead employs iText as a library and uses that library to convert a JFreeChart-generated chart into a PDF.

The first step after downloading the iText JAR is to place it in the build and runtime classpaths or in the IDE's project. Using iText to write a chart into a PDF is not much more difficult than downloading the JAR file and placing it in the classpath. The following two code listings show the code for creating a ring chart (it is similar to a pie chart and uses the PieDataset) and then the code for writing that produced chart out to PDF.

Listing 11: Creating a Ring Chart


 
 /**
 * Create ring chart indicating number of countries in each region.
 * 
 * @return Ring (similar to Pie) Chart with number of countries per region.
 */
 public JFreeChart createCountriesPerRegionRingChart()
 {
 JFreeChart ringChart = null;
 
 try
 {
 final String QUERY_NUMBER_COUNTRIES_PER_REGION =
 "SELECT regions.region_name, count(*) AS num_countries " +
 " FROM regions, countries " +
 "WHERE regions.region_id = countries.region_id " +
 "GROUP BY regions.region_name";
 
 final PieDataset ringDataset =
 new JDBCPieDataset( databaseAccess.getOracleDbConnection(),
 QUERY_NUMBER_COUNTRIES_PER_REGION );
 
 ringChart =
 ChartFactory.createRingChart(
 TITLE_COUNTRIES_PER_REGION, // title
 ringDataset, // pie (ring) dataset
 true, // legend displayed
 true, // tooltips displayed
 false ); // no URLs
 
 }
 catch (SQLException sqlEx)
 {
 // exception handling code here . . . 
 }
 
 return ringChart;
 }
 

The above code listing looks very similar to other code listings in this article that used JFreeChart to generate a chart. The next code listing shows how this or any other chart generated with JFreeChart can be written to a PDF. The generated JFreeChart (the ring chart created in the last listing in this case) can be passed to the method below, along with a String containing the filename to which the PDF file should be written.

Listing 12: Rendering a JFreeChart Generated Chart in PDF


 
 
 /**
 * Write PDF based on the provided chart.
 * 
 * @param aChart Chart to be written out as PDF.
 * @param aOutputPdfFileName File name (without extension) to write PDF file.
 * @param aWidth Width of chart to be rendered.
 * @param aHeight Height of chart to be rendered.
 */
 public void writePdfBasedOnChart( JFreeChart aChart,
 String aOutputPdfFileName,
 int aWidth,
 int aHeight )
 {
 final String fileExtension = ".pdf";
 
 // com.lowagie.text.Document
 Document document = new Document();
 try
 {
 PdfWriter.getInstance( document,
 new FileOutputStream( aOutputPdfFileName
 + fileExtension));
 document.open();
 Image png = Image.getInstance(
 ChartUtilities.encodeAsPNG(
 aChart.createBufferedImage(aWidth,aHeight)));
 document.add(png);
 document.close();
 }
 catch (DocumentException docEx) // checked exception
 {
 docEx.printStackTrace();
 }
 catch (IOException ioEx) // checked exception
 {
 ioEx.printStackTrace();
 }
 }
 
 

As indicated in the code listing above, only six iText-related Java code statements are needed to write the provided chart into a PDF. These steps are as follows:

  • Create an iText Document (from the com.lowagie.text package).
  • Open the iText Document with the open() method.
  • Instantiate an iText Image with the static Image.getInstance() method call, receiving the chart in byte[] form.
  • Add the Image to the iText Document, using its add() method.
  • Close the iText Document, using its close() method.

Figure 11 is a screen shot of the PDF produced by the above code.

Figure 11: Adobe Reader showing PDF containing generated ring chart

JFreeChart's support for writing a chart out as a BufferedImage and in byte[] format makes it very straightforward to transform the chart into a format that is readily convertible to PDF with iText.

Example 8: Minor Image Customization

Although the servlet example showed some chart creation customization, most of the previous examples in this article used JFreeChart-generated charts directly without any customization. JFreeChart enables developers to customize charts at the cost of greater complexity. This example shows some fairly easy customizations, but far-more-powerful (and complicated) customizations are available. In fact, customization is virtually unlimited if one is willing to write enough code.

In this example, the produced line chart will have a transparent background (transparency support is one of the PNG format's many advantages) and will have its series paint color changed from the default red to blue. The line chart is probably not the best type of chart for displaying the quantity of each of the Oracle Database object types in the HR schema, but using a line chart provides us an opportunity to demonstrate JFreeChart's 3-D line chart capabilities.

Three images are shown here. The first (Figure 12) is a screen shot from using the Web-based administration tool that comes with Oracle Database XE and shows the number of schema objects in the HR schema by object type. The next image (Figure 13) displays a line chart generated by JFreeChart out-of-the-box, using the default colors (gray background and red paint) without any customization, and the third image (Figure 14) shows a customized JFreeChart chart based on the same data. Both JFreeChart-generated images show the respective charts rendered in a browser with the background color intentionally set to a darker shade to make the transparency of the customized chart's background more obvious.

The chart shown in Figure 14 has a transparent background, and this is easiest to see when that figure is compared with Figure 13. However, only the outside of the chart is transparent. The portion of the plot with the actual graphs, called the Plot, is white in both cases. The plot's background can have its color changed in the same way that the overall chart's background was changed. The setBackgroundPaint method is called on the Plot in the same way that it is called on the chart itself to generate Figure 14.

Figure 12: Oracle Database XE Web tool display of HR database objects

Figure 13: Line chart illustrating quantity of each object in the HR schema

Figure 14: Same chart as that displayed in Figure 13, but with transparent background and blue stroke color

The next code listing shows the code for generating the customized 3-D line chart.

Listing 13: Generating PNG File with Background Transparency


 
 /**
 * Write .png file with transparent background based on provided JFreeChart.
 * 
 * @param aChart JFreeChart.
 * @param aFileName Name of file to which JFreeChart will write chart. 
 * @param aWidth Width of image.
 * @param aHeight Height of image.
 */
 public void writePngTransparentBasedOnChart( JFreeChart aChart,
 String aFileName,
 int aWidth,
 int aHeight )
 {
 final String fileExtension = ".png";
 try
 {
 aChart.setBackgroundPaint( new Color(255,255,255,0) );
 
 CategoryItemRenderer renderer = aChart.getCategoryPlot().getRenderer();
 renderer.setSeriesPaint(0, Color.blue.brighter());
 renderer.setSeriesVisible(0, true); // default
 renderer.setSeriesVisibleInLegend(0, true); // default
 
 ChartUtilities.writeChartAsPNG(
 new FileOutputStream(aFileName + fileExtension),
 aChart,
 aWidth, aHeight,
 null,
 true, // encodeAlpha
 0 );
 }
 catch (IOException ioEx)
 {
 System.err.println( "Error writing PNG file "
 + aFileName + fileExtension);
 }
 }
 

Example 9: Using XML to Provide Input Data for JFreeChart

The final example demonstrates creating a JFreeChart based on XML data. We will use Oracle Database support for returning data from tables in XML form as part of this example. Normally, we would not want to convert relational data to XML for JFreeChart processing when we could have simply used a JDBCDataset to process the relational data directly. However, this relational-to-XML feature is useful in this example, because it enables us to use the HR schema to simulate XML-oriented datasources. There are many sources that might provide data in XML format, including XML databases (such as Oracle databases storing XML documents rather than the traditional normalized relational data), XML web services, and other products and libraries.

For this example, we will be gathering information from the HR schema about employee commission rates. We are assuming here that an employee with a null commission value in the table is not compensated with commissions and so are charting only the commission rates of those who do receive commissions. The normal relational data query for this is shown in Listing 14.

Listing 14: SQL SELECT Used for Commissions Query


 
 SELECT commission_pct, count(commission_pct)
 FROM employees
 WHERE commission_pct IS NOT NULL
 GROUP BY commission_pct
 ORDER BY commission_pct;
 
 

The query in Listing 14 returns the number of employees receiving each commission level (percentage). Employees who do not get paid a commission are not included in the totals.

The XML data we need to provide to JFreeChart for rendering our chart needs to meet the XML grammar expected by JFreeChart. In Listing 15, we show a modified version of the query first outlined in Listing 14 that partially meets the XML grammar expectations of JFreeChart.

Listing 15: Using Column Aliases to Match Query Results to JFreeChart Expected XML


 
 SELECT commission_pct AS "Key"
 count(commission_pct) AS "Value" 
 FROM employees
 WHERE commission_pct IS NOT NULL
 GROUP BY commission_pct
 ORDER BY commission_pct
 
 

This new version of the query aliases (or renames) the columns returned in the SELECT statement. Instead of commission_pct and count(commission_pct) as the column headings, we have aliased these to now be called Key and Value, respectively.

Even with the Listing 15 modifications to the query, we still do not retrieve the data in XML format. Listing 16 provides the Java code for using OracleResultSet to retrieve the data from the database in XML format.

Listing 16: Obtaining Data from Oracle Database as XML


 
 
 /**
 * Get commission percentage breakdown from database.
 */
 public String getCommissionPercentageBreakdownAsXml()
 {
 final String QUERY_NUMBER_EACH_COMMISSION_LEVEL =
 "SELECT commission_pct AS \"" + DatasetTags.KEY_TAG + "\", " +
 "count(commission_pct) AS \"" + DatasetTags.VALUE_TAG + "\" " +
 "FROM employees " +
 "WHERE commission_pct IS NOT NULL " +
 "GROUP BY commission_pct " +
 "ORDER BY commission_pct";
 
 OracleXMLQuery qry =
 new OracleXMLQuery( getOracleDbConnection(),
 QUERY_NUMBER_EACH_COMMISSION_LEVEL);
 qry.setRowTag(DatasetTags.ITEM_TAG);
 qry.setRowsetTag(DatasetTags.PIEDATASET_TAG);
 
 return qry.getXMLString();
 }
 

In Listing 16, we used JFreeChart-provided static constants (DatasetTags.KEY_TAG and DatasetTags.VALUE_TAG) rather than hard-coded values for the two column headings (Key and Value), so that this query will remain synchronized with any changes to these constants in future versions of JFreeChart.

Listing 16 exhibits the use of OracleXMLQuery to obtain the data from the query in XML format. The setRowTag and setRowsetTag calls are significant, because they enable us to easily change the row tags and rowset tags to match what JFreeChart expects. As before, we use JFreeChart-provided constants (DatasetTags.ITEM_TAG and DatasetTags.PIEDATASET_TAG) to specify the names of these tags. Finally, the getXMLString method is called on our instance of OracleXMLQuery to return the results of all of this in well-formed XML. Listing 17 displays the contents of this generated XML String.

Listing 17: JFreeChart-compliant XML Generated from Oracle Database Query


 
 <?xml version = '1.0'?> 
<PieDataset>  
<Item num="1"> 
<Key>0.1</Key>  
<Value>6</Value> 
</Item>   
<Item num="2">   
<Key>0.15</Key>    
<Value>5</Value>  
</Item>    
<Item num="3">  
<Key>0.2</Key>  
<Value>7</Value> 
</Item> 
<Item num="4">  
<Key>0.25</Key>  
<Value>6</Value>   
</Item>  
<Item num="5">  
<Key>0.3</Key>   
<Value>7</Value>   
</Item>    
<Item num="6">   
<Key>0.35</Key>    
<Value>3</Value>  
</Item>   
<Item num="7"> 
<Key>0.4</Key>    
<Value>1</Value>  
</Item>
</PieDataset>
 

As mentioned above, DatasetTags constants have been used to provide the tag names PieDataset, Item, Key, and Value, which JFreeChart expects. Thanks to the OracleXMLQuery class, the ability to alias return column names, and the JFreeChart-provided constants, we now have an XML document prepared to populate a JFreeChart pie chart.

Listing 18 demonstrates use of JFreeChart's DatasetReader to ingest appropriately prepared XML as input data for chart generation.

Listing 18: Using DatasetReader to Process XML Input for Chart Generation


 
 /**
 * Create 3D pie chart displaying the number of employees at each
 * commission level. This method demonstrates use of DatasetReader to
 * translate data from XML format to JFreeChart data format.
 * 
 * @return 3D Pie Chart showing number of employees at each commission
 * level.
 */
 public JFreeChart createCommissionsPercentagePerLevelPieChart()
 {
 PieDataset pieData = null;
 
 final String xmlData =
 databaseAccess.getCommissionPercentageBreakdownAsXml();
 
 final DatasetReader reader = new DatasetReader();
 try
 {
 pieData = reader.readPieDatasetFromXML(
 new ByteArrayInputStream(xmlData.getBytes()) );
 }
 catch (IOException ioEx)
 {
 ioEx.printStackTrace();
 }
 
 JFreeChart chart =
 ChartFactory.createPieChart3D(
 "Commissioned Employees at Each Commission Level",
 pieData,
 false, true, false );
 
 // Chart specifically provides getCategoryPlot() and getXYPlot() methods,
 // but not a getPiePlot() or getPiePlot3D() method.
 final PiePlot3D plot = (PiePlot3D) chart.getPlot();
 plot.setDepthFactor(0.35); // pie depth 35% of plot height
 plot.setForegroundAlpha(0.5f); // Declare explicitly as float (50%)
 plot.setLabelGenerator(new HrCommissionsPieGenerator());
 
 return chart;
 }
 

The method called on DatasetReader in Listing 18 was specifically designed for PieDataset implementations, as evidenced by the method name (readPieDatasetFromXML). The other type of Dataset currently supported by JFreeChart's DatasetReader is the CategoryDataset (for bar charts, line charts, and the like).

The instance of JFreeChart returned by the method in Listing 18 behaves like the instances of this same class shown in all of the previous examples. In other words, this JFreeChart produced from XML input data can be saved as a PNG or JPEG file, can be rendered in a Swing application, can be rendered in a servlet or JSP page, and can even be rendered as an SVG image or within a PDF file.

Figure 15 displays the PNG rendered from the JFreeChart produced in Listing 18.

Figure 15: 3-D pie chart generated from XML source data

When you are building a chart from database data, it is often easier to do so directly from the database results than by first converting to XML and then building the dataset. This is because the XML generation adds an extra step to the process and because the convenient JDBC Datasets support a wider variety of chart types than does the DatasetReader for XML. Whereas the DatasetReader supports PieDataset and CategoryDataset, there is a JDBC dataset for x,y-based charts (JDBCXYDataset) in addition to JDBC datasets for pie (JDBCPieDataset) and category (JDBCCategoryDataset) charts.

The JFreeChart support for XML input data is useful when the data to be charted is available "natively" in XML. Even then, however, it is likely that the data will not be in the format JFreeChart expects. Therefore, the source XML data will need to be transformed via XSLT, XQuery, or custom processing to the expected XML format. Also, the XML support provides an easy way to write static data for chart generation as flat files that can be easily edited by hand and then ingested for building a chart.

In addition to demonstrating use of XML to provide data for generating a chart with JFreeChart, this example also demonstrates additional customization possibilities with JFreeChart. In this case, the specific type of plot (PiePlot3D) was retrieved. This plot inherits from PiePlot and adds a few methods specific to its 3-D nature (such as the depth setting applied in the example). This PiePlot3D was then used to add some transparency to the pie slices, to make the slices deeper, and to customize the labels for the slices. Comparing the output in Figure 15 with previous pie charts in this article demonstrates how this customization affects the final chart.

Listing 18 includes a reference to an HrCommissionsPieGenerator class. This class, whose definition is shown in Listing 19, overrides the labels for the slices in the produced pie chart.

Listing 19: Customizing Pie Chart Labels


 
 
 public class HrCommissionsPieGenerator implements PieSectionLabelGenerator
 { 
 /**
 * Generates a customized label for a pie section of pie chart depicting
 * commissions levels.
 * 
 * @param aDataset Dataset destined for pie chart.
 * @param aKey The identifying key for each section of the pie chart.
 * 
 * @return Customized label for section of pie chart identified by aKey.
 */
 public String generateSectionLabel(final PieDataset aDataset,
 final Comparable aKey)
 {
 String labelResult = null;
 if (aDataset != null)
 {
 labelResult = (int)(Double.parseDouble(aKey.toString())*100) +
 "% sales (" + aDataset.getValue(aKey).intValue() +
 " employees)";
 }
 
 return labelResult;
 }
 }
 

Although we have provided nine examples to demonstrate the power and flexibility of JFreeChart, it has many other aspects. The next section outlines some of JFreeChart's attractive features that are not highlighted in this article.

Additional JFreeChart Features

JFreeChart offers many features and chart types not covered here. Among them are the following:

  • The types of charts generated and displayed in this article are only a subset of the types of charts JFreeChart generates out-of-the-box. Many of the other chart types are related to financial reporting chart types, such as Box and Whisker, Bubble, Candlestick, Gantt, High Low, Polar, Scatter Plot, Wafer Map, Waterfall, and Wind Plot. One of the easiest ways to identify the automatically available chart types is to use JDeveloper's code completion (CTRL+SPACE) on the ChartFactory class and select one of the "create…" methods from the drop-down list (see Figure 16). The length of the scroll bar indicates the plethora of other available charts.

Figure 16: Using JDeveloper autocompletion to identify JFreeChart chart types

  • Even with all of the chart types offered by JFreeChart, there are situations when additional flexibility is required to render the perfect chart. Although JFreeChart makes it straightforward to create many chart types, it also offers developers the opportunity to deal with additional complexity and significantly customize the provided chart types. This article has demonstrated only a small portion of this chart customization capability. JFreeChart exposes lower-level APIs (AWT/Java2D) to give developers additional flexibility when they need it.
  • JFreeChart (and Cewolf) provide mechanisms for generating tooltips for generated charts.
  • JFreeChart supports creation of HTML image maps (see the documentation for the ChartUtilities class).
  • JFreeChart can be used in Standard Widget Toolkit (SWT)-based applications. This article covered an example involving Swing, but JFreeChart can also be used with SWT.

Summarizing Key Advantages of JFreeChart

There are several compelling reasons to start using JFreeChart to produce data-centric charts. Here are some of the most significant of these reasons:

  • License/purchase cost – Free. Even if you factor in the relatively modest price of the recommended JFreeChart Developer Guide, the cost is still extremely low and difficult to beat. JFreeChart is distributed under the GNU Lesser General Public License.
  • Powerful features – Free or low-cost licensing is insufficient reason to select a library or framework if that framework or library lacks required and desirable features. JFreeChart supports many different types of charts and supports rendering of these charts in many different formats and environments.
  • Simplicity – Powerful, feature-rich libraries often come at the expense of great complexity and high learning curves. JFreeChart, however, provides an API that is remarkably easy to learn and use and supports powerful features. Creating basic charts with JFreeChart is very straightforward, and creating more-complex charts requires only incremental experience with the JFreeChart API. The JDBC-oriented Datasets make it particularly easy to render charts based on data stored in a relational database.
  • Support for several versions of Java – There seems to be a mad rush to inject annotations, generics, and other features of J2SE 5 and Java SE 6 into third-party frameworks, libraries, and tools. Although these features provide attractive benefits, they preclude developers from taking advantage of the new frameworks or libraries if their baseline version of Java predates J2SE 5. The minimum version of Java required for JFreeChart 1.0.6 (the latest version at the time of this writing) is JDK 1.3. This is significant, because developers using Java 1.4.2 can use the latest version of JFreeChart. Conversely, JFreeChart works readily with Java SE 6, and all the examples in this article were compiled and run against Java SE 6. Finally, as demonstrated in this article, JFreeChart can be readily applied to chart generation in both the Java Standard Edition (SE) and Java Enterprise (EE) environments.

Obtaining Additional Information on JFreeChart

This article is intended to provide an introduction to many of the features of JFreeChart. However, JFreeChart is a powerful and feature-rich library that cannot be thoroughly covered in a single article. There are several resources developers can access for more information about JFreeChart, most of which can be accessed from JFreeChart's home page.

The Javadoc documentation for JFreeChart classes and interfaces is a good starting point for going beyond this introductory article. The online Javadoc-based documentation links to HTML pages (Javadoc "linksource" style) with the actual JFreeChart code when you drill down far enough within the links. This can sometimes be an easier method of examining the code than opening the source code files themselves.

We used JFreeChart-provided constants several times in our examples. These can be conveniently located at www.jfree.org/jfreechart/api/javadoc/constant-values.html. Review of these constants provides insight regarding JFreeChart default values and settings.

The online JFreeChart forum also provides an archive of previous questions and answers regarding JFreeChart and allows developer questions. For a modest fee, developers can purchase and download the JFreeChart Developer Guide for JFreeChart coverage that is much more thorough.

Conclusion

JFreeChart provides a free, open source alternative for producing high-quality charts that represent database or other data in graphical form. The library is easy to learn and use, produces many different types of charts, and supports rendering of these charts in many different environments in standard and enterprise Java.


Dustin Marx is a Senior Software Engineer at Raytheon Co.
Michael G. Martin Principal Software Engineer at Raytheon Co.