Recently, I needed to demonstrate the capabilities of JasperReports library to one of our clients. JasperReports is an open source reporting engine written in Java which can use various kind of data sources (JDBC, XML, CSV etc.) and produce documents that can be viewed, printed or exported in variety of formats such as HTML, PDF, Excel and many more.
I didn’t want to create just a simple Java application which uses the library. There`s no fun in that. Instead, I wanted to integrate it into an already existing application. Our new product GRACE was the perfect candidate for this task. With little know-how touch from our ADF gurus in Dreamix and my hands were on the keyboard, ready to get the job done. Here is how the magic works.
First we need to add the JasperReports library in the classpath of our ADF application. You can download the library from SourceForge . I assume you have created or imported an existing ADF project in JDeveloper, so right click on ViewController and select “Project Properties”. Go to “Libraries and Classpath” and add jasperreports-xxx.jar, where xxx is the version you have downloaded.
Next, create a folder called “reports” under “Web Content” directory. It will contain previously designed reports which will be later generated to HTML or PDF. If you are interested in designing a report, you may check this tutorial .
In ViewController, right click on “Application Source” and select “New” -> “Java class”. The name of the new class will be JasperService which will be in eu.dreamix.services package. JasperService contains methods for generating a report into HTML or PDF format and one method for extracting the parameters of a report, which will be used to dynamically create ADF input controls. Here is the code for JasperService class:
[java]
package eu.dreamix.services;
import java.io.OutputStream;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.view.JasperViewer;
[/java]
[java]
public class JasperService {
JasperReport jasperReport;
JasperPrint jasperPrint;
JasperViewer jasperViewer;
Connection con = null;
Context ctx = null;
DataSource ds = null;
Map<String, Object> parameters = new HashMap();
String url = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/reports/").toString().trim();
//The url string is the folder where a designed report is deployed
JRParameter[] param;
public JasperService() {
super();
}
public Connection getDataSource() {
try {
ctx = new InitialContext();
ds = (DataSource) ctx.lookup("MyDS");
//this is the data source name used from the WebLogic server
con = ds.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return con;
}
public void close(Connection con) {
if (con != null) {
try {
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//this method is responsible for extracting the parameters needed by a report
public JRParameter[] getReportParameters(String reportName) {
try {
jasperReport = JasperCompileManager.compileReport(url + "/" + reportName + ".jrxml");
param = jasperReport.getParameters();
} catch (JRException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return param;
}
//this method generates HTML page out of a report
public void runJasperHTML(String reportName, String[] reportInputControlsValues) {
try {
int index = 0;
if (reportInputControlsValues != null) {
for (JRParameter filteredParam : getReportParameters(reportName)) {
if (filteredParam.isForPrompting() && !filteredParam.isSystemDefined()) {
parameters.put(filteredParam.getName(), reportInputControlsValues[index]);
++index;
}
}
}
jasperReport = JasperCompileManager.compileReport(url + "/" + reportName + ".jrxml");
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, getDataSource());
JasperExportManager.exportReportToHtmlFile(jasperPrint, url + "/" + reportName +".html");
System.out.println("Done!");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("closing connection");
close(con);
}
}
//this method generates PDF document out of a report
public void runJasperPDF(String reportName, String[] reportInputControlsValues, OutputStream outputStream) {
try {
int index = 0;
if (reportInputControlsValues != null) {
for (JRParameter filteredParam : getReportParameters(reportName)) {
if (filteredParam.isForPrompting() && !filteredParam.isSystemDefined()) {
parameters.put(filteredParam.getName(), reportInputControlsValues[index]);
++index;
}
}
}
jasperReport = JasperCompileManager.compileReport(url + "/" + reportName + ".jrxml");
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, getDataSource());
JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
System.out.println("Done!");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("closing connection");
close(con);
}
}
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
}
[/java]
Next we need to create a JasperGenericBean class responsible for executing a report and dynamically creating input controls if the submitted report contains some. Here is the code:
[java]
package eu.dreamix.jasperreports;
import eu.dreamix.util.ADFUtils;
import eu.dreamix.services.JasperService;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import net.sf.jasperreports.engine.JRParameter;
import oracle.adf.view.rich.component.rich.layout.RichPanelFormLayout;
[/java]
[java]
public class JasperGenericBean {
private String reportPath;
private JasperService jasperService;
private Boolean iFrameVisible = false;
private Boolean parameterFormVisible = false;
private String reportName;
private Map<String, String> reportValues;
private RichPanelFormLayout paramsPanel;
public JasperGenericBean() {
}
public void setReportName(String reportName) {
this.reportName = reportName;
}
public String getReportName() {
return reportName;
}
public void setJasperService(JasperService jasperService) {
this.jasperService = jasperService;
}
public JasperService getJasperService() {
return jasperService;
}
public void setReportPath(String reportPath) {
this.reportPath = reportPath;
}
public String getReportPath() {
return reportPath;
}
public void setParamsPanel(RichPanelFormLayout paramsPanel) {
this.paramsPanel = paramsPanel;
}
public RichPanelFormLayout getParamsPanel() {
return paramsPanel;
}
public void setReportValues(Map<String, String> reportValues) {
this.reportValues = reportValues;
}
public Map<String, String> getReportValues() {
return reportValues;
}
public void setIFrameVisible(Boolean iFrameVisible) {
this.iFrameVisible = iFrameVisible;
}
public Boolean getIFrameVisible() {
return iFrameVisible;
}
public void setParameterFormVisible(Boolean parameterFormVisible) {
this.parameterFormVisible = parameterFormVisible;
}
public Boolean getParameterFormVisible() {
return parameterFormVisible;
}
public void init(){
reportName = “CustomerLoans”; //this is the name of the report located in the reports folder
JasperService jasperService = new JasperService();
reportPath = "https://localhost:7101/f2a/demo/reports/" +reportName +".html";
//this is only a PoC, don’t judge me 🙂
boolean hasParams = addInputControls();
if(hasParams){
iFrameVisible = false; //this iFrame will hold the generated report in HTML format
parameterFormVisible = true;
}else{
jasperService.runJasperHTML(reportName, null);
iFrameVisible = true;
}
}
public boolean addInputControls(){
reportValues = new HashMap<String, String>();
jasperService = new JasperService();
for (JRParameter filteredParam : jasperService.getReportParameters(reportName)) {
if (filteredParam.isForPrompting() && !filteredParam.isSystemDefined()) {
reportValues.put(filteredParam.getName(), null);
}
}
return reportValues.size() > 0;
}
public void executeReport(ActionEvent ae){
String[] paramArray = new String[reportValues.size()];
int index = 0;
for(Map.Entry v : reportValues.entrySet()){
System.out.println(v.getKey() + " "+v.getValue());
paramArray[index] = (String)v.getValue();
++index;
}
jasperService.runJasperHTML(reportName, paramArray);
iFrameVisible = true;
}
public void exportToPDF(FacesContext facesContext, OutputStream outputStream) {
String[] paramArray = new String[reportValues.size()];
int index = 0;
for(Map.Entry v : reportValues.entrySet()){
System.out.println(v.getKey() + " "+v.getValue());
paramArray[index] = (String)v.getValue();
++index;
}
jasperService.runJasperPDF(reportName, paramArray, outputStream);
iFrameVisible = true;
}
}
[/java]
The next step is to create an ADF page fragment (jsff page) and put it in a TaskFlow. The page will be used to display the input controls, the values of which will be passed as parameters during the compilation of a report. The page will hold also two buttons – one for HTML generation and one for downloading the report as a PDF document. Here is the code of our jasperGeneric.jsff
[java]
<?xml version=’1.0′ encoding=’UTF-8′?>
<ui:composition xmlns_ui="https://java.sun.com/jsf/facelets" xmlns_af="https://xmlns.oracle.com/adf/faces/rich"
xmlns_f="https://java.sun.com/jsf/core" xmlns_h="https://java.sun.com/jsf/html">
<af:panelSplitter orientation="vertical" splitterPosition="128" id="ps1">
<f:facet name="first">
<af:panelFormLayout id="pfl1" binding="#{pageFlowScope.jasperGenericBean.paramsPanel}"
rendered="#{pageFlowScope.jasperGenericBean.parameterFormVisible}">
<af:forEach items="#{pageFlowScope.jasperGenericBean.reportValues}" var="entry">
<af:inputText label="#{entry.key}"
value="#{pageFlowScope.jasperGenericBean.reportValues[entry.key]}"
id="it1#{entry.key}"/>
</af:forEach>
<af:commandButton text="Submit" actionListener="#{pageFlowScope.jasperGenericBean.executeReport}"
id="cb1"/>
<af:button text="Download as PDF" id="b1">
<af:fileDownloadActionListener method="#{pageFlowScope.jasperGenericBean.exportToPDF}"
contentType="application/x-download" filename="#{pageFlowScope.jasperGenericBean.reportName}.pdf"/>
</af:button>
</af:panelFormLayout>
</f:facet>
<f:facet name="second">
<af:inlineFrame source="#{pageFlowScope.jasperGenericBean.reportPath}" sizing="preferred" id="if1"
visible="#{pageFlowScope.jasperGenericBean.IFrameVisible}" partialTriggers="cb1"/>
</f:facet>
</af:panelSplitter>
</ui:composition>
[/java]
We are ready to deploy the application on the WebLogic server and try it out!
I hope this PoC of how to integrate JasperReports library into ADF application will be helpful. Feel free to use or modify the code, ask questions or add comments.