Using dynamically generated JFreeChart’s charts and JasperReports

If you are reading this blog it means you want to use JasperReport with dynamically programmatically generated images. An example of such use cases in generating complex charts using JFreeChart and then including these charts into reports.

Each report in JasperReport uses a datasource to populate the fields, we need to use JRBeanCollectionDataSource which can be considered as a simple collection of JavaBeans. Each object in the JRBeanCollectionDataSource can be used to populate one row of the report or it can be used to prepare calculated values and so on.

Each Report in JasperReport is basically an XML file which describe the report. in our case the report description document is like:



<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="DataSourceReport" pageWidth="595" pageHeight="842" columnWidth="515" leftMargin="40" rightMargin="40" topMargin="50" bottomMargin="50">
	<style name="Sans_Normal" isDefault="true" fontName="DejaVu Sans" fontSize="12" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false"/>
	<style name="Sans_Bold" isDefault="false" fontName="DejaVu Sans" fontSize="12" isBold="true" isItalic="false" isUnderline="false" isStrikeThrough="false"/>
	<style name="Sans_Italic" isDefault="false" fontName="DejaVu Sans" fontSize="12" isBold="false" isItalic="true" isUnderline="false" isStrikeThrough="false"/>
	<field name="image" class="java.awt.image.BufferedImage"/>
	<field name="description" class="java.lang.String"/>
	<detail>
		<band height="180">
			<image scaleImage="RetainShape" hAlign="Center" isUsingCache="true" isLazy="true">
				<reportElement x="67" y="17" width="344" height="140"/>
				<imageExpression class="java.awt.Image"><![CDATA[$F{image}]]></imageExpression>
			</image>
			<textField>
				<reportElement x="83" y="157" width="311" height="20"/>
				<textElement/>
				<textFieldExpression class="java.lang.String"><![CDATA[$F{description}]]></textFieldExpression>
			</textField>
		</band>
	</detail>
</jasperReport>


As you can see I defined two fields named image and description which are two property of the ReportBean objects included in the data source. The report looks as follow in the IreportDesigner (I am using NetBeans plugin for developing reports).

report in the designer

Lets see what is the JavaBean class we want to use to carry the report fields from our Java code to the JasperReport engine.

 public class ChartBean {      public ChartBean(BufferedImage image, String description) { setImage(image); setDescription(description);     }           private java.awt.image.BufferedImage image;     private String description;      public String getDescription() {         return description;     }      public void setDescription(String description) {         this.description = description;     }      public BufferedImage getImage() {         return image;     }      public void setImage(BufferedImage image) {         this.image = image;     }   } 

Now we need to prepare the datasource for the report, to prepare the datasource I wrote a method like the following snippet which uses another method to extract the BufferedImage from the JFreeChart items which we want to include in the report.

     public JRBeanCollectionDataSource prepareDataSource() {           List charts = new ArrayList();

        for (int i = 0; i < 5; i++) {
            JFreeChart chart = createChart("This is chart number: " + i);
            BufferedImage bi = extractImage(chart, 800, 600);
            ChartBean chartBean = new ChartBean(bi, "This is description for Chart: " + i);
            charts.add(chartBean);
        }

        return new JRBeanCollectionDataSource(charts);
    }

The createChart method create some sample charts which we will include in the report. the method is simpley creating some dummy ring charts without any specific and meaningful data.

 private JFreeChart createChart(String chartTitle) {           DefaultPieDataset piedataset = new DefaultPieDataset();         piedataset.setValue("One", new Double(43.200000000000003D));         piedataset.setValue("Two", new Double(10D));         piedataset.setValue("Three", new Double(27.5D));         piedataset.setValue("Four", new Double(17.5D));         piedataset.setValue("Five", new Double(11D));         piedataset.setValue("Six", new Double(19.399999999999999D));           JFreeChart jfreechart = ChartFactory.createRingChart(chartTitle, piedataset, false, true, false);          RingPlot ringplot = (RingPlot) jfreechart.getPlot();         ringplot.setLabelFont(new Font("SansSerif", 0, 12));         ringplot.setNoDataMessage("No data available");         ringplot.setSectionDepth(0.34999999999999998D);         ringplot.setCircular(false);         ringplot.setLabelGap(0.02D);         return jfreechart;     } 

The other method which need to be explained is extractImage which simply create a BufferedImage from a given JfreeChart object.

  public BufferedImage extractImage(JFreeChart chart, int width, int height) {         BufferedImage img =                 new BufferedImage(width, height,                 BufferedImage.TYPE_INT_RGB);          Graphics2D g2 = img.createGraphics();        chart.draw(g2, new Rectangle2D.Double(0, 0, width, height));          g2.dispose();         return img;      } 

Finally I have a method named showReport which uses the created datasource to prepare a report and then uses JRViewer to show the report. The code snippet is as follow:

   public void showReport(JRBeanCollectionDataSource dataSource) {          try {             Map parameters = new HashMap();             JasperDesign jasperDesign = JRXmlLoader.load(this.getClass().getResourceAsStream("/jasperdynamic/report.jrxml"));             JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);             JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);             JRViewer viewer = new JRViewer(jasperPrint);             viewer.setSize(850, 500);             viewer.setMinimumSize(new Dimension(850, 510));             viewer.setPreferredSize(new Dimension(850, 510));              this.setLayout(new BorderLayout(10, 10));              this.setSize(850, 500);              this.getContentPane().add(viewer);             this.setVisible(true);          } catch (Exception ex) { ex.printStackTrace();         }     } 

And the complete code for the application application class is as follow:

 /*  * To change this template, choose Tools | Templates  * and open the template in the editor.  */ package jasperdynamic;  import javax.swing.JFrame; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import net.sf.jasperreports.engine.design.JasperDesign; import net.sf.jasperreports.engine.xml.JRXmlLoader; import net.sf.jasperreports.view.JRViewer; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.RingPlot; import org.jfree.data.general.DefaultPieDataset;  /**  *  * @author masoud  */ public class Main extends JFrame {      /**      * @param args the command line arguments      */     public static void main(String[] args) {         Main m = new Main();          m.showReport(m.prepareDataSource());      }      public JRBeanCollectionDataSource prepareDataSource() {           List charts = new ArrayList();

        for (int i = 0; i < 5; i++) {
            JFreeChart chart = createChart("This is chart number: " + i);
            BufferedImage bi = extractImage(chart, 800, 600);
            ChartBean chartBean = new ChartBean(bi, "This is description for Chart: " + i);
            charts.add(chartBean);
        }

        return new JRBeanCollectionDataSource(charts);
    }

    public BufferedImage extractImage(JFreeChart chart, int width, int height) {
        BufferedImage img =
                new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);

        Graphics2D g2 = img.createGraphics();
       chart.draw(g2, new Rectangle2D.Double(0, 0, width, height));

        g2.dispose();
        return img;

    }

    public void showReport(JRBeanCollectionDataSource dataSource) {

        try {
            Map parameters = new HashMap();
            JasperDesign jasperDesign = JRXmlLoader.load(this.getClass().getResourceAsStream("/jasperdynamic/report.jrxml"));
            JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
            JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
            JRViewer viewer = new JRViewer(jasperPrint);
            viewer.setSize(850, 500);
            viewer.setMinimumSize(new Dimension(850, 510));
            viewer.setPreferredSize(new Dimension(850, 510));

            this.setLayout(new BorderLayout(10, 10));

            this.setSize(850, 500);

            this.getContentPane().add(viewer);
            this.setVisible(true);

        } catch (Exception ex) {
ex.printStackTrace();
        }
    }

    private JFreeChart createChart(String chartTitle) {


        DefaultPieDataset piedataset = new DefaultPieDataset();
        piedataset.setValue("One", new Double(43.200000000000003D));
        piedataset.setValue("Two", new Double(10D));
        piedataset.setValue("Three", new Double(27.5D));
        piedataset.setValue("Four", new Double(17.5D));
        piedataset.setValue("Five", new Double(11D));
        piedataset.setValue("Six", new Double(19.399999999999999D));


        JFreeChart jfreechart = ChartFactory.createRingChart(chartTitle, piedataset, false, true, false);

        RingPlot ringplot = (RingPlot) jfreechart.getPlot();
        ringplot.setLabelFont(new Font("SansSerif", 0, 12));
        ringplot.setNoDataMessage("No data available");
        ringplot.setSectionDepth(0.34999999999999998D);
        ringplot.setCircular(false);
        ringplot.setLabelGap(0.02D);
        return jfreechart;
    }
}


Following image shows how the report will look like after we run the program. and the complete source code can be downloaded from Here

report in the designervar

So you want to develop a rich client application on top of NetBeans RCP?

I was involved with development of a RCP application based on NetBeans platform and now I find few minutes to share some of the experience with you.

All standard coding and best practices are applicable here. Use project management systems like Trac or any project management system that you know. Never start a project without a project management system. Use coding standard, unit testing, desing and implementation documents versioning, design discussion sessions and so on.

Prepare all library wrappers and give them proper names. In NetBeans RCP we can not add JAR files to modules and instead JAR files need to be wrapped inside library wrapper modules and then this modules can be included in list of other modules dependencies. Use proper names for library wrappers, for example: jfreechart-wrapper-1.0.13. Never think about having one fat wrapper which contains all required libraries.

Avoid including the same JAR files in more than one wrapper which will be used by one module. instead include all common JAR files in one wrapper and include that wrapper in the dependencies of each wrapper which need it.

Think ahead and decide which modules you want to have and how these modules depends on each other. For example: A module which holds the domain mode and persistence layer (it will be wrapper inside a library wrapper if persistence layer is based on JPA), a module which contains all Web Services access objects (Again, this is a Java SE project wrapped inside a library wrapper), a module for reporting, a module for security implementation, a module to host all utility classes and common resources like icons, graphics, configuration files, etc.

Avoid circular dependencies. You can not access Security module from persistence and then using persistence objects from security module. Best practice is to define some logical layer and ensure that each layer can only access the layer under it and not any other layer.

If acceptable by customer, prepare an update center and push updates using the update center to avoid over working. The update center will help with distributing the software updates which can be new modules, updates, bug fixes etc.
Keep these FAQs close as they will help you along the way:
http://wiki.netbeans.org/NetBeansDeveloperFAQ
http://deadlock.netbeans.org/hudson/job/faqsuck/lastSuccessfulBuild/artifact/other/faqsuck/build/faq.html

JPA and a First tier application performance problems

Well, this is the second time I am using the new blogging platform of Java.net. This time it was much easier and with less hurdle.

I want to write about JPA, performance, caching and related things. Though it wont be very well organized but it may come useful for new JPA developers. recently I have assigned a task to work on performance problems of a first tier application and further extend it with some new forms and reports and address some usability glitch in the software. The application was developed by a company which no longer exists. The developer company was dissolved before they finishing the development cycles.

The performance issue was very important for the customer, so I start investigating the performance problem first. First things first, I run the application on client machine and hell it was slow, very slow indeed. it took some 15 seconds for the application to open a Jframe with two auto completing JCombobox on it. I though maybe it is the first time I am accessing the database and application is trying to initialize a soft client side cache. So, I closed the JFrame and open it again. the result was almost the same. I checked network and DNS resolving was fast and no substantial network delay was in place. I thought the problem is either in a poor development or something is wrong on database machine.

I asked an operator about the performance and I understand that is a data access problem. and I should focus on that area.

I opened a terminal to Database server machine and checked how much load it is taking when I open that particular JFrame, well there was no substantial load on the server. indeed there was no load on the server. I briefly check to see what kind of indexing we have on two tables where I though are used to load items of JComboBox/se and hell, No index was created. The developers either was crazy not to have any index except for the default PK index or they have forgotten to execute their post installation script.

I asked for the source code of the application to see what is going on in the application code. The application is based on TopLink Essential which is a part of GlassFish project, contributed by Oracle for data access. I had some previous experience with Toplink and I was sure that there is nothing wrong with the persistence provider itself.

Application source code was documented but not enough to understand the code easily. I locate that JFrame form and checked how they are loading the content of JCombobox/es. Well, they were calling an static method from a DAO class which returns a list an Object named A then it was iterating over the list and loading the JComboBox with A.property1 of. I though why on earth they are fetching the entire object to use on property?

I checked the second JC and I found the same steps, A DAO method were called which returns a list and then the JC was loaded using D.property1.
I start looking at these two entities to see what is going on with them and I find that each of them has a one to many relation with at least one other entities. So we basically had something like:

A —–1—–N—–>B——1———-N——>C
D —–1—–N—–>E

There were no FetchType determined for the relationships so, Toplink assume FetchType.EAGER for the relationships by default. The FetchType.EAGER means that JPA eagerly loads all related records from database and create all corresponding objects before we even start using them.
What does that means? It Means that for the first JComboBox that client application creates num(A)+num(B)+num(C)+num(A)*num(B)*num(C) objects just to show a list of names in a JComboBox. Very funny hu?

I create a development database setup the development environment (NetBeans works well with ANT based projects) and start changing the code. I didn’t change the fetch types instead I changed the JPA queries to ensure that I am only loading one field and not all fields which have eager fetching relationships. I compiled and problem for this particular form were solved. I added two indexes to ensure that I am getting the result as fast as possible.

I was thinking about JPA caching and why JPA was not able to improve the overall performance after I closed the form and re-opened it. I think the data load was more than what JPA could cache by default.

I will blog more about these application and its problems. I believe it was either a prototype of the developers were intended to reiterate over the code to improve the performance.

My refcard for Oracle Berkeley DB Java edition published by DZone

The refcard discuss the following items: 

  1. The BDB Family : An introduction to different DBD family members, including BDB Base Edition, BDB XML edition and BDB Java Edition with tables comparing their features.
  2. Key Features: Key features of BDB family members and BDB Java Edition exclusive features are explained here
  3. Introducing Berkeley DB Java Edition includin:

  • Installation: How to install the BDB JE and which JAR files are required to be in class path.
  • Description of Access APIs: Three different types of access APIs are explained and a working sample for each type of access APIs is included. Discussed APIs are Base API (Key/Value pair store and retrieval), DPL (Direct Persistence Layer for object persistence), Collections API(Using the BDB JE as an on-disk collection

        4. BDB Java Edition environment anatomy: How BDB JE store information on hard disk and what is directory and file layout of BDB JE is discussed here
5. BDB Java Edition APIs characteristics: What makes BDB JE different and how these differences can help developers in different scenarios.
6. Transaction Support: explanation and sample code for using Transaction with Base API and DPL
7. Persisting Complex Object Graph using DPL: How to define relation between objects using annotation and how to persist and retrieve related objects is explained here
8. BDB JE Backup/Recovery and Tuning: How to create backup, restore the backup and what tuning factor can be used to fine tune the BDB JE is explained here
9. Helper Utilities: Helper utilities for exporting and importing data are explained here
You can download the refcard free of charge from here.

 

You will find several tables and illustrations to make understanding the the whole concept of BDB JE and differences between BDB JE, BDB XML Edition and BDB Base Edition better. All included sample codes are fully explained.