Search code examples
javajasper-reportsdynamic-reports

Got empty output file with DynamicReports using JasperReportBuilder


I am using DynamicReports 4.1.1. since I depend on Java 1.6. The problem is, that all my reports are empty no matter which export format is being used. The pdfs have about 1600 Bytes, so there is no problem with writing as such. But no content. I have read that this can happen if the datasource ist empty, but this is not the case here. Has someone an idea?

 private void build() {
    try {
        JRDataSource c = new JRBeanCollectionDataSource(createDataSource());
        report()
                .setTemplate(Templates.reportTemplate)
                .columns(
                col.column("Item", "item", type.stringType()),
                col.column("Quantity", "quantity", type.integerType()),
                col.column("Unit price", "unitPrice", type.bigDecimalType()))
                .title(Templates.createTitleComponent("CollectionDatasource"))
                .detailFooter(cmp.line())
                .pageFooter(Templates.footerComponent)
                .noData(Templates.createTitleComponent("NoData"), cmp.text("There is no data"))
                .setDataSource(c);
        report().toPdf(new FileOutputStream("report4.pdf"));
    } catch (DRException e) {
        e.printStackTrace();
    }
}

private List<Data> createDataSource() {
    List<Data> data = new ArrayList<Data>();
    data.add(new Data("DVD", 5, new BigDecimal(30)));
    data.add(new Data("Book", 8, new BigDecimal(11)));
    data.add(new Data("PDA", 2, new BigDecimal(15)));
    return data;
}

private class Data {

    private String item;
    private Integer quantity;
    private BigDecimal unitPrice;

    public Data(String item, Integer quantity, BigDecimal unitPrice) {
        this.item = item;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
    }

    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public BigDecimal getUnitPrice() {
        return unitPrice;
    }

    public void setUnitPrice(BigDecimal unitPrice) {
        this.unitPrice = unitPrice;
    }

    @Override
    public String toString() {
        return "Data{" + "item=" + item + ", quantity=" + quantity + ", unitPrice=" + unitPrice + '}';
    }
  }

Solution

  • What is wrong?

    You made a minor mistake (looks like copy&paste issue) - you are generating code from not properly initialized JasperReportBuilder object.

    You created two instances of JasperReportBuilder class:

    report()
            .setTemplate(Templates.reportTemplate)
            .columns(
               col.column("Item", "item", type.stringType()),
               col.column("Quantity", "quantity", type.integerType()),
               col.column("Unit price", "unitPrice", type.bigDecimalType()))
            .title(Templates.createTitleComponent("CollectionDatasource"))
            .detailFooter(cmp.line())
            .pageFooter(Templates.footerComponent)
            .noData(Templates.createTitleComponent("NoData"), cmp.text("There is no data"))
            .setDataSource(c); // here is a first instance. It is initialized via builde. You should use instance for any actions
    report().toPdf(new FileOutputStream("report4.pdf")); // the second instance. Just "blank" object, you are missed initializing via builder
    

    The valid code in your case will be:

    report()
            .setTemplate(Templates.reportTemplate)
            .columns(
               col.column("Item", "item", type.stringType()),
               col.column("Quantity", "quantity", type.integerType()),
               col.column("Unit price", "unitPrice", type.bigDecimalType()))
            .title(Templates.createTitleComponent("CollectionDatasource"))
            .detailFooter(cmp.line())
            .pageFooter(Templates.footerComponent)
            .noData(Templates.createTitleComponent("NoData"), cmp.text("There is no data"))
            .setDataSource(c)
            .toPdf(new FileOutputStream("report4.pdf")); // we are not breaking the chain
    

    or:

    JasperReportBuilder report = report()
        .setTemplate(Templates.reportTemplate)
        .columns(
           col.column("Item", "item", type.stringType()),
           col.column("Quantity", "quantity", type.integerType()),
           col.column("Unit price", "unitPrice", type.bigDecimalType()))
        .title(Templates.createTitleComponent("CollectionDatasource"))
        .detailFooter(cmp.line())
        .pageFooter(Templates.footerComponent)
        .noData(Templates.createTitleComponent("NoData"), cmp.text("There is no data"))
        .setDataSource(c); // we are created design, set styles and datasource and so on for our builder
    
    report.toPdf(new FileOutputStream("report4.pdf"));  // we are using the valid builder prepared at previous step
    

    Details

    It is just a builder pattern, no magic here.

    Let's look at source code of DynamicReports.report() method.

    public class DynamicReports {
        // some members
    
        public DynamicReports() {
        }
    
        public static JasperReportBuilder report() {
            return new JasperReportBuilder();
        }
    

    As you can see that the new object created every time we call DynamicReports.report() method. There is no singleton or static member here.