Wednesday, October 31, 2012

Accessing Jasper Reports from JSF application

I was asked by one of our client if I can prepare a reporting platform which they can access from the internet. So I made the following simple demo to show them how it can be implemented. I thought it nice to share with others. I will assume that you have JSF, hibernate and iReports knowledge. The demo is just how I put the technologies together.

Tools Used
  1. Netbeans 7.2
  2. iReports 4.7.1
  3. Oracle 10g XE database
  4. Glassfish*
  5. Primefaces*
  6. Hibernate*
*These come bundled with Netbeans IDE

Designing the Report Layout

The idea for the mock report was to show employees grouped by their manager. We will use Oracle SCOTT schema which comes bundled with Oracle databases.
To make the reports creation easy I created the report layout with the SQL from the report I will use. This is nice since I could preview my report with the data I will put. I trust you can create a report in iReport however I will attach the JRXML file for convenience of creating a similar report layout. I called the report file,
  1: EmployeesReportToJSFApp.jrxml


Which I compiled in iReport to a .jasper file.

The JSF Application



Just to make the application function like a normal report we will add a p: datatable to show the report elements before exporting it. The advantage of using Netbeans IDE is that it comes bundled with Primefaces, Hibernate and Glassfish server. We will create a new Web Project. I called mine ScottReportsOne but you can call it anything. I add framework JSF2 and choose Primefaces as the faces components. We will also add hibernate and a data source connection to our Oracle XE database.


Then Netbeans will create two JSF files, index.xhtml and welcomePrimefaces.xhtml and a hibernate.cfg.xml file for hibernate data source.


Managed Beans, DAOs and Entities


Our report will use a native query to populate the report. We will add the following entity to the database. Note this is just a normal POJO file and it does not have to be registered anyway in hibernate configuration. or any xml file for that matter.


EmployeesReport.java


  1: package com.tamla.model.entities;
  2: 
  3: public class EmployeesReport {
  4: 
  5:     private java.math.BigDecimal empNumber;
  6:     private String empName;
  7:     private String jobTitle;
  8:     private String empManager;
  9:     //getters and setters
 10: 
 11: }


We will create a the query to populate the file in,


EmployeeReportDao.java


  1: public class EmployeeReportDao implements Serializable{
  2: 
  3:     Session session = HibernateUtil.getSessionFactory().openSession();
  4: 
  5:     public List<EmployeesReport> pullReportAllEmployees() {
  6:         String queryString = "select a.empno as \"empNumber\", a.ename as \"empName\", a.job as \"jobTitle\",\n"
  7:                 + "       b.ename as \"empManager\"\n"
  8:                 + "  from emp a, emp b\n"
  9:                 + " where b.empno = a.mgr order by 4";
 10:         Query query = session.createSQLQuery(queryString).setResultTransformer(Transformers.aliasToBean(com.tamla.model.entities.EmployeesReport.class));
 11:         List<EmployeesReport> result = query.list();
 12:         return result;
 13:     }
 14: }


Notice that we have to create a HibernateUtil.java class first for this piece of code to work. In Netbeans this can be created automatically for you. We use hibernate transformers to transform the list from the query into a collection list of EmployeesReport class.


Next we create a managed bean to stitch the data objects to the JSF views. The managed bean is as below.


ReportsManagedBean.java


  1: @ManagedBean
  2: @ViewScoped
  3: public class ReportsManagedBean{
  4: 
  5:     private List<EmployeesReport> employeesReportList;
  6:     private JasperPrint jasperPrint;
  7: 
  8:     public ReportsManagedBean() {
  9:     }
 10: 
 11:     public void populateReport() {
 12:         EmployeeReportDao dao = new EmployeeReportDao();
 13:         employeesReportList = dao.pullReportAllEmployees();
 14:     }
 15: 
 16:     public List<EmployeesReport> getEmployeesReportList() {
 17:         return employeesReportList;
 18:     }
 19: 
 20:     
 21:     public void setEmployeesReportList(List<EmployeesReport> employeesReportList) {
 22:         this.employeesReportList = employeesReportList;
 23:     }
 24: 
 25:     public void reportBuilder() throws JRException {
 26:         JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(employeesReportList);
 27:         String report = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/resources/reports/EmployeesReportToJSFApp.jasper");
 28:         jasperPrint = JasperFillManager.fillReport(report, new HashMap(), beanCollectionDataSource);
 29:     }
 30: 
 31:     public void exportToPdf(ActionEvent actionEvent) throws JRException, IOException {
 32:         reportBuilder();
 33:         HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
 34:         httpServletResponse.addHeader("Content-disposition", "attachment; filename=Employees_List.pdf");
 35:         ServletOutputStream servletStream = httpServletResponse.getOutputStream();
 36:         JasperExportManager.exportReportToPdfStream(jasperPrint, servletStream);
 37:         FacesContext.getCurrentInstance().responseComplete();
 38:     }
 39: 
 40:     public void exportToXlsx(ActionEvent actionEvent) throws JRException, IOException {
 41:         reportBuilder();
 42:         HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
 43:         httpServletResponse.addHeader("Content-disposition", "attachment; filename=Employees_List.xlsx");
 44:         ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
 45:         JRXlsxExporter docxExporter = new JRXlsxExporter();
 46:         docxExporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
 47:         docxExporter.setParameter(JRExporterParameter.OUTPUT_STREAM, servletOutputStream);
 48:         docxExporter.exportReport();
 49:         FacesContext.getCurrentInstance().responseComplete();
 50:     }
 51: }
 52: 



If  you don’t have the Jasper reports dependencies you should download them and add to the classpath. The required dependencies generating PDF and XLS jasper reports are.

  • jasperreports
  • commons-beanutils
  • commons-collections
  • commons-digester
  • commons-javaflow
  • commons-logging
  • groovy-all
  • iText
  • poi


Note all these dependencies can be downloaded together with the jasper reports community library from here. Just click on the download library button and they require you to login to access the downloads.


JSF


The java part is done next is the JSF. For the JSF we will only show the data in a p:datatable.  The table and the reports structure for this minimum example are different. For JSF instead of creating a new file for the demo I decided to use the already created template from Netbeans.


welcomePrimefaces.xhtml


  1: <p:layoutUnit position="center">   
  2:                     <p:panel header="Employee Report" style="font-size: 12px;">
  3:                         <h:form>
  4:                             <p:commandButton value="Populate Table" actionListener="#{reportsManagedBean.populateReport()}" update="myTable" />
  5:                             <p:dataTable value="#{reportsManagedBean.employeesReportList}" var="report" id="myTable"  >
  6:                                 <f:facet name="header">
  7:                                     List of Employees 
  8:                                 </f:facet>
  9:                                 <p:column >
 10:                                     <f:facet name="header">
 11:                                         Employee No.
 12:                                     </f:facet>
 13:                                     <h:outputText value="#{report.empNumber}" style="font-size: 12px;"  />                   
 14:                                 </p:column>
 15:                                 <p:column  >
 16:                                     <f:facet name="header">
 17:                                         Employee Name
 18:                                     </f:facet>
 19:                                     <h:outputText value="#{report.empName}" style="font-size: 12px;"  />                   
 20:                                 </p:column>                                
 21:                                 <p:column  >
 22:                                     <f:facet name="header">
 23:                                         Job Title
 24:                                     </f:facet>
 25:                                     <h:outputText value="#{report.jobTitle}" style="font-size: 12px;"  />                   
 26:                                 </p:column>
 27:                                 <p:column >
 28:                                     <f:facet name="header">
 29:                                         Reports To
 30:                                     </f:facet>
 31:                                     <h:outputText value="#{report.empManager}" style="font-size: 12px;"  />                   
 32:                                 </p:column>
 33:                             </p:dataTable>
 34:                             <p:toolbarGroup align="center" >
 35:                                 <p:commandButton value="Export To PDF" id="exportToPdfButton" actionListener="#{reportsManagedBean.exportToPdf}" ajax="false" />
 36:                                 <p:spacer width="10"/>
 37:                                 <p:commandButton value="Export To Excel" id="exportToXlsxButton" actionListener="#{reportsManagedBean.exportToXlsx}" ajax="false" />                
 38:                             </p:toolbarGroup>
 39:                         </h:form>
 40:                     </p:panel>
 41:                 </p:layoutUnit>


Notice that I only changed the p:layoutunit position=”center” to add all this. The remainder I did not change. Also note that we have put the jasper report file in the following folder ScottReportsOne\web\resources\reports\EmployeesReportToJSFApp.jasper. This file is the compiled reports file.


Running the Example



What the system is doing is just to fill the data into the report display and populating the template. A running war file with a working example can be downloaded here. Happy building reporting platforms with JSF and JasperReports.

5 comments:

  1. Hi,

    I already had a long time looking for something like this!!

    Thank you so much!!!

    Regards

    ReplyDelete
  2. Please link running the sample don't work

    ReplyDelete
  3. I have this exception:

    java.lang.ClassCastException: org.apache.catalina.core.ApplicationContextFacade cannot be cast to javax.servlet.http.HttpServletResponse

    ReplyDelete
  4. Really helpful.
    thanx for this tuturial

    ReplyDelete