Wednesday, January 27, 2010

Crystal Reports Integration with Struts2

A task came along to integration Crystal Reports with struts2. After searching the web I came across a solution provided to integrate Crystal Reports with struts.

http://www.jroller.com/njain/entry/crystal_reports_plus_struts

Couldn't find one with struts2. So i changed it according to my requirements and after doing it successfully, I thought of sharing it with others.


The JSP is



<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://displaytag.sf.net" prefix="display" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<%@ taglib prefix="jscalendar" uri="/jscalendar" %>
<html>
    <head>
        <title>Crystal Report 1</title>
  
    </head>
    <body bgcolor="#FFFFFF" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" >
        <s:form id="frm" name="frm" action="GenerateCyrstalReport1.action" target="_blank">      
        <tr>
            <td background="images/main_back.jpg" align="center" class="displayTag"><h4>Crystal Report</h4></td>
        </tr>
        <tr>
            <td>
                <table>
                    <tr>
                        <td width="5%"></td>
                        <td width="10%"><s:label value="From" /></td>
                        <td width="25%"><jscalendar:jscalendar  name="startDate" format="%d/%m/%Y" showstime="false"  /></td>
                        <td width="75%"></td>
                    </tr>
                    <tr>
                        <td width="5%"></td>
                        <td width="10%"><s:label value="To" /></td>
                        <td width="25%"><jscalendar:jscalendar name="endDate" format="%d/%m/%Y" showstime="false"  /></td>
                        <td width="75%"></td>
                    </tr>
                    <tr>
                        <td colspan="3">    </td>  
                          <td width="30%"><input type="submit"" value="Submit" Class="Button"  /></td>
                    </tr>
                </table>
            </td>
        </tr>
        </s:form>          
    </body>
</html>



I created a base class(coded below) which will do all the dirty work to creating a report. All CrystalReport action classes will extend this class.
This base class extends
    -    ActionSupport     
and implements three interfaces.
    -    ServletRequestAware
    -    ServletResponseAware
    -    ServletContextAware

i implemented these three interfaces because CrystalReports requires the request, response and servletContext variables. This is an abstract class because it has two abstract methods (getReport(), getParamterFields()) which will be implemented by the subclass.


/**
 *
 */
package project.action;

import java.util.Locale;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.util.ServletContextAware;

import com.opensymphony.xwork2.ActionSupport;

import com.crystaldecisions.report.web.viewer.CrystalReportViewer;
import com.crystaldecisions.report.web.viewer.ReportExportControl;
import com.crystaldecisions.reports.reportengineinterface.JPEReportSourceFactory;
import com.crystaldecisions.sdk.occa.report.data.Fields;
import com.crystaldecisions.sdk.occa.report.data.ParameterField;
import com.crystaldecisions.sdk.occa.report.data.ParameterFieldDiscreteValue;
import com.crystaldecisions.sdk.occa.report.data.Values;
import com.crystaldecisions.sdk.occa.report.exportoptions.ExportOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.ReportExportFormat;
import com.crystaldecisions.sdk.occa.report.reportsource.IReportSource;
import com.crystaldecisions.sdk.occa.report.reportsource.IReportSourceFactory2;

/**
 * @author mustansar.samad
 *
 */
public abstract class CrystalReportAction extends ActionSupport implements ServletRequestAware, ServletResponseAware, ServletContextAware {
  
  
    private HttpServletRequest request;
    private HttpServletResponse response;
    private ServletContext servletContext;
  
    public String execute() throws Exception {
  
        return SUCCESS;
    }
  
  
    protected abstract String getReportName();
  
    public String generateReport() throws Exception {
      
        createReportInCrystal();
        //createReportInPDF();
      
        return SUCCESS;
    }
  
    private void createReportInCrystal() throws Exception {
      
        CrystalReportViewer reportViewer = createReportViewerForCrystal(getReportName(), request.getLocale());
        try {
            reportViewer.processHttpRequest(request, response, getServletContext(), response.getWriter());
        }
        catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        finally {
            reportViewer.dispose();
        }
    }
  
    private CrystalReportViewer createReportViewerForCrystal(String reportName, Locale locale) throws Exception {
      
        //Creating a report Viewer
        CrystalReportViewer reportViewer = new CrystalReportViewer();
      
        //Creating source for the report
        IReportSourceFactory2 rptSrcFactory = new JPEReportSourceFactory();
        IReportSource reportSource = (IReportSource) rptSrcFactory.createReportSource(reportName, locale);
      
        //Setting the source to the reportViewer
        reportViewer.setReportSource(reportSource);
      
        //Setting paramenter Values returned an abstract method which is implemented by the Child Class
        reportViewer.setParameterFields(getParamterFields());
      
        //Setting global properties for report viewer for all reports
        setReportViewerForCrystalProperties(reportViewer);

        return reportViewer;
    }
  
    private void setReportViewerForCrystalProperties(CrystalReportViewer reportViewer) {
        reportViewer.setHasRefreshButton(false);
        reportViewer.setHasExportButton(true);
        reportViewer.setEnableParameterPrompt(false);
        reportViewer.setEnableLogonPrompt(true);
        reportViewer.setHasLogo(false);
    }
  
  
    private void createReportInPDF() throws Exception {
      
        ReportExportControl exportControl = createReportExporterForPDF(getReportName(), request.getLocale());
        try {
            exportControl.processHttpRequest(request, response, getServletContext(), null);
        }
        catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        finally {
            exportControl.dispose();
        }
    }
  
  
    private ReportExportControl createReportExporterForPDF(String reportName, Locale locale) throws Exception {
      
        //Creating a report exporter
        ReportExportControl exportControl = new ReportExportControl();
      
        //Creating source for the report
        IReportSourceFactory2 rptSrcFactory = new JPEReportSourceFactory();
        IReportSource reportSource = (IReportSource) rptSrcFactory.createReportSource(reportName, locale);
      
        //Setting the source to the exportControl
        exportControl.setReportSource(reportSource);
      
        //Setting paramenter Values returned an abstract method which is implemented by the Child Class
        exportControl.setParameterFields(getParamterFields());
      
        //Setting global properties for export control for all reports
        setReportExporterForPDFProperties(exportControl);
      
        return exportControl;
      
    }
      
  
    private void setReportExporterForPDFProperties(ReportExportControl exportControl) {
      
        //PDFExportFormatOptions PDFOpts = new PDFExportFormatOptions();
        //PDFOpts.setStartPageNumber(1); 
        //PDFOpts.setEndPageNumber(5);
      
        ExportOptions exportOptions = new ExportOptions();
        exportOptions.setExportFormatType(ReportExportFormat.PDF);
        //exportOptions.setFormatOptions(PDFOpts);
        exportControl.setEnableParameterPrompt(false);
        exportControl.setExportOptions(exportOptions);
        exportControl.setExportAsAttachment(false);
    }
  
  
  
    protected abstract Fields getParamterFields() throws Exception;
  
    public ParameterField newParameterField(String name, Object dataValue) {
  
        ParameterField field = new ParameterField();
        field.setName(name);
        field.setReportName("");
  
        ParameterFieldDiscreteValue pfieldDV1=new ParameterFieldDiscreteValue();
      
        pfieldDV1.setValue(dataValue);
      
        Values vals1 = new Values();
        vals1.add(pfieldDV1);
      
        field.setCurrentValues(vals1);
      
        return field;
    }
  
  
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }
    public HttpServletRequest getRequest() {
        return request;
    }

    public void setServletResponse(HttpServletResponse response) {
        this.response = response;
      
    }
    public HttpServletResponse getResponse() {
        return this.response;
    }

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
      
    }
    public ServletContext getServletContext() {
        return this.servletContext;
    }

}



This is the Action classes which extends the base class defined above. Note that there is no execute() method in this class, it will use execute() method of parent class. I used the same class for loading the filter screen and for creating the report. So execute() method runs for the first time i.e. doing nothing, just returning "SUCCESS" to navigate to filter screen page which is configured in struts.xml. The action to generate report calls the generateReport() method which is also in the parent class.

This class implements two abstract methods of the parent class.
-    getReportName()            //returns the name of the report located in WebContent folder of my application.
-    getParamterFields()        //returns the parameters required to set for the Crystal Reports

startDate and endDate are the values the action class gets from the jsp  as String.

in getParamterFields()
-    I am manually converting String into Date bacause the parameters are set as Date in the Report. (was having problem getting values as Date from JSP, so I kept them String)


package project.action;

import project.AppUtility;

import com.crystaldecisions.sdk.occa.report.data.Fields;
import com.crystaldecisions.sdk.occa.report.data.ParameterField;

/**
 * @author mustansar.samad
 *
 */
public class CrystalReport1Action extends CrystalReportAction {

    private String startDate;
    private String endDate;
  
    public String getStartDate() {
        return startDate;
    }
    public void setStartDate(String startDate) {
        this.startDate = startDate;
    }
  
    public String getEndDate() {
        return endDate;
    }
    public void setEndDate(String endDate) {
        this.endDate = endDate;
    }
  
    @Override
    protected String getReportName() {
        return "Report2.rpt";
    }
  
    @Override
    protected Fields getParamterFields() throws Exception {
        Fields fields = new Fields();
        ParameterField parameterField = newParameterField("param1", AppUtility.convertToDate(getStartDate()));
        ParameterField parameterField1 = newParameterField("param2", AppUtility.convertToDate(getEndDate()));
        fields.add(parameterField);
        fields.add(parameterField1);
      
      
        return fields;
    }
  
  
}

package project;

This is the utilityClass to convert string to Date.

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author mustansar.samad
 *
 */
public class AppUtility {
  
    private static DateFormat appDateFormat = new SimpleDateFormat("dd/MM/yyyy");
  
    public static Date convertToDate(String date) throws Exception {
        try {
            return appDateFormat.parse(date);
        }
        catch(Exception e) {
            return null;
        }
    }


generateReport() method of parent class is called to create the report which then depends on getReportName() and getparameterFields() methods to create a report. The rest of the process is same for all the reports.
there are two methods in generateReport()
-    createReportInCrystal();
-    createReportInPDF();
either one can be used to create a different type of report.


Happy Programming!

1 comment:

  1. what will be the struts.xml configuration?
    And how to show report in jsp page using Struts2?

    ReplyDelete