Java with Crystal Reports
引用自:http://forum.java.sun.com/thread.jspa?threadID=543125&messageID=2703479Integrating Crystal Reports with a J2EE Application:A Case StudyIntroductionIn this paper, we discuss our experience with integrating Crystal Reports with a J2EEapplication, Task Workbench (TWB). The goal of this paper is to help others evaluate thebenefits and drawbacks of using Crystal Reports in the context of a J2EE application.Task Workbench (www.taskworkbench.com) is a work management tool that provides real-timeinformation about projects and day-to-day operations. It is a web application that uses JSPs forthe presentation layer and EJBs for the business logic. Persistence is via container managedpersistence (CMP) Entity Enterprise Java Beans (EJBs) mapped to an Oracle relational database.TWB helps project managers stay informed about the progress of projects by providing variousways to track the number of hours that employees spend on each project. When designing thethis application, an important requirement was to provide project managers and administrativestaff with a high-level view of the team member and project data.Figure 1: Task Workbench displays an employeeýs tasks and the hours spent on each task.Initially we created reports in TWB using JSP, however this approach proved to be prohibitivelytime consuming, especially for complex reports, so a Java reporting solution for webapplications was sought. Our basic requirements were that the solution should make it fast andeasy to create and modify reports, and that it could be integrated with a J2EE application withminimum implementation effort.We selected the Crystal Reports Java Reporting Component because it met all of ourrequirements. It is also a mature, industry-leading product with a range of tools available forit. This was an important consideration because we wanted to ensure that our reportingsolution could grow as TWB's requirements expanded.Ensemble Systems Inc.Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 2 of 13www.ensemble-systems.comThe first section of this paper briefly describes the original report implementation using handcodedJSP, and the problems that we encountered with this approach. The next sectionexplains why we selected the Crystal Java Reporting Component as our reporting solution. Theremainder of the paper discusses how we integrated Crystal Reports with TWB, and details thepros and cons of using Crystal Reports.Original Report ImplementationThe early versions of TWB implemented reports using hand-coded JSP. In most cases, thereport data used JDBC to connect directly to the database for performance reasons. Anadditional advantage of implementing reports in this way was that the queries already existedfor a precursor application, and these complex queries had been proved to be correct.This approach quickly became problematic for a number of reasons:ý Query optimization was difficult since the queries had to be dynamically altered, basedon multiple report settings.ý The implementation effort for reports with grouped data proved to be prohibitive.ý We had no practical way to rapidly prototype reports for cases where the requirementswere unclear or unstable. This discouraged experimentation and lead to certain reportsnot adequately meeting the customerýs requirements.ý Maintenance became very time-consuming as the schema changed and new featureswere introduced.ý Customization of the output of the reports for specific uses (e.g. printing) was timeconsuming.Our initial implementation used CMP entity EJBs to obtain the data for reports. Theshortcomings of this naýve implementation quickly become apparent ý in particular, it did notscale well for large datasets and a significant amount of work would have been required toaddress this deficiency.These issues made it clear that we needed a much better solution to our reportingrequirements; we needed a way to create more sophisticated reports with much less effort. Weturned to Crystal Reports for this solution.Selecting a Reporting SolutionOur difficulties with creating reports with JSP in TWB caused us to look to better reportingsolutions. Our requirements were that the reporting solution should:ý Have a visual report editor to make it fast and easy to create and modify reports.ý Generate highly presentable reports, both on the screen and when printed.ý Include the ability to generate grouped reports with graphs.ý Obtain the report data via an instance of javax.sql.DataSource, or a JDBC connection.ý Be a pure Java solution.We considered open source alternatives such as Jasper Reports. However, we found that noneof the open source options met our basic requirement for a visual report editor and we judgedthem unlikely to provide adequately sophisticated solutions without a lot of additional work.We elected to use a solution based on Crystal Reports, because we had been impressed withCrystal Reports when we used it with an unrelated project.Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 3 of 13www.ensemble-systems.comThere are three classes of Crystal Reporting solutions: Java Reporting Component, ReportApplication Server (RAS), and Crystal Enterprise. The Java Reporting Component provides asimple, pure Java solution for viewing reports in cases where there is likely to be lowconcurrent usage and where report management functionality is not required. The RAS providesa basic set of services for processing, viewing and modifying reports locally and remotely. Sincereport processing is done on demand, RAS is best suited for smaller datasets and small numbersof concurrent users. Crystal Enterprise is a set of products with features designed to supportlarge installations with more demanding requirements. The basic features of Crystal Enterpriseinclude load management (clustering, report scheduling, etc), availability (fail-over), androw/column level security.We selected the Crystal Java Reporting Component for TWB since this solution met all ourrequirements and we anticipated that there would be a small number of concurrent users forreports. We also did not need the advanced report interactivity options as users would onlyrequire basic report viewing and printing capabilities.ImplementationThe Crystal Java Reporting Component was integrated into TWB with emphasis on two keygoals: The first was to provide an implementation that required the least possible effort toadd new reports and maintain existing reports. The second goal was to make it simple to switchto a more sophisticated Crystal reporting solution, such as Crystal Enterprise, in order toprovide for scalability if required.The Crystal Java Reporting Component has a JSP tag library that provides a simple way todisplay basic reports; direct access to the API is provided for cases where more features arerequired. We chose to implement our own custom JSP tag that uses the Crystal ReportingComponent to display the reports. This approach allowed us to customize the appearance ofthe reports and to set report parameters while keeping the design simple and easy to maintain.The simplicity of this approach is illustrated by the fact that the only Java code required in theJSP is a single method call to a factory class to obtain an instance of the report to be displayedon the page. Figure 2, simple_banked_time_report.jsp provides a basic example of a JSP thatmight be used to request a report. This form passes the required parameters to view_report.jsp(Figure 3).Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 4 of 13www.ensemble-systems.com<%@ page import="com.ensemsys.twb.presentation.crystal.ReportParameter,com.ensemsys.twb.presentation.crystal.ReportType"%><html><head><title>Report Viewer</title><link rel="stylesheet" href="style/main.css" type="text/css"/></head><body><h1>Simple Banked Hours Report</h1><p>Enter the date range for the report and click "Submit" to viewthe report.</p><form method="post" action="view_report.jsp"><input type="hidden" name="<%=ReportParameter.TYPE%>"value="<%=ReportType.BANKED_TIME_SIMPLE%>"/><table><tr><td class="label">Start Date:</td><td><input type="text" name="<%=ReportParameter.START_DATE%>"value="2003.06.30"/></td></tr><tr><td class="label">End Date:</td><td><input type="text" name="<%=ReportParameter.END_DATE%>"value="2003.12.01"/></td></tr><tr><td> </td><td style="text-align: right"><input type="submit" name="submitBtn" value="Submit"/></td></tr></table></form></body></html>Figure 2: simple_banked_time_report.jsp, an example of a simple form that can be used todisplay a report.Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 5 of 13www.ensemble-systems.com<%@ page import="com.ensemsys.twb.presentation.crystal.ReportFactory,com.ensemsys.twb.presentation.crystal.Report"%><%@ taglib uri="/simplereportviewer.tld" prefix="viewer" %><html><head><title>Report</title><link rel="stylesheet" href="style/main.css" type="text/css"/></head><body><%// "CrystalEventTarget" is the report action name used by Crystalfinal String REPORT_NAME = "CrystalEventTarget";String reportName = (String) session.getAttribute( REPORT_NAME );// refresh might have done via a report actionif ( reportName == null ){reportName = (String) request.getParameter( REPORT_NAME );}Report report = null;if ( reportName == null ){report = ReportFactory.newInstance( request );reportName = report.getName();session.setAttribute( REPORT_NAME, reportName );session.setAttribute( reportName, report );}else{report = (Report) session.getAttribute( reportName );}%><viewer:viewer report="<%=report%>" /></body></html>Figure 3: view_report.jsp, the JSP used to display a report.The class ReportFactory creates an instance of Report, using the parameters in the request todetermine the type of report to be created and to create the report's parameters (see Figure4). This approach neatly encapsulates the business logic specific to individual reports in onemethod. Factory methods for more complex types of reports are best implemented in asubclass of ReportFactory - the ReportFactory can then delegate to the subclass when creatingthat type of report.Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 6 of 13www.ensemble-systems.comFigure 4: ReportFactory.java creates an instance of a Report, based on parameterssubmitted from a form.package com.ensemsys.twb.presentation.crystal;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 javax.servlet.ServletRequest;import javax.servlet.jsp.JspException;import java.sql.Date;import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateFormat;/*** Provides methods for creating an instance of a Report* from the parameters in the request.*/public class ReportFactory{public static final DateFormat FORMAT =new SimpleDateFormat( "yyyy.MM.dd" );public static Report newInstance( ServletRequest request )throws JspException{String reportType = getParameter( request, ReportParameter.TYPE );Report report = null;// For brevity, only the simple report type is shown hereif ( reportType.equals( ReportType.BANKED_TIME_SIMPLE ) ){report = newSimpleBankedTimeReport( request );}else{throw new InvalidReportTypeException( reportType );}return report;}Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 7 of 13www.ensemble-systems.comFigure 4: ReportFactory.java (continued).private static Report newSimpleBankedTimeReport( ServletRequest request )throws JspException{Date startDate = getDateParameter( request, ReportParameter.START_DATE );Date endDate = getDateParameter( request, ReportParameter.END_DATE );Fields fields = new Fields();ParameterField prevYearField = newDateField( "Prev Fiscal Year", startDate);ParameterField reportDateField = newDateField( "Report Date", endDate );fields.add( prevYearField );fields.add( reportDateField );return new Report( "protected/reports/bhr_simple_demo.rpt",ReportType.BANKED_TIME_SIMPLE, fields );}public static ParameterField newDateField( String name, Date date ){ParameterField field = new ParameterField();Values vals = new Values();ParameterFieldDiscreteValue value = new ParameterFieldDiscreteValue();field.setName( name );value.setValue( date );field.setReportName( "" );value.setDescription( "" );vals.add( value );field.setCurrentValues( vals );return field;}public static Date newDate( String s )throws InvalidDateStringException{java.sql.Date date = null;try{date = new java.sql.Date( FORMAT.parse( s ).getTime() );}catch ( ParseException e ){throw new InvalidDateStringException( e );}return date;}protected static void assertHasParameter( ServletRequest request, String name){String parameter = request.getParameter( name );if ( parameter == null || parameter.equals( "" ) ){throw new NoSuchParameterException( name );}}Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 8 of 13www.ensemble-systems.comFigure 5: SimpleReportViewerTag.java is the implementation of the custom JSP tag fordisplaying a Report.protected static Date getDateParameter( ServletRequest request, String name )throws JspException{String parameter = getParameter( request, name );Date date = null;try{date = newDate( parameter );}catch ( InvalidDateStringException e ){throw new JspException( e );}return date;}protected static String getParameter( ServletRequest request, String name ){assertHasParameter( request, name );return request.getParameter( name );}}Figure 4: ReportFactory.java (continued).The class SimpleReportViewerTag (Figure 5) renders the report; this class uses the Crystal JavaReporting Component API to perform the drawing of the report. This class is quitestraightforward - it creates an instance of CrystalReportViewer, sets the database connectioninformation and the parameters that the report requires, and then displays the report. Thecorresponding TLD, simplereportviewer.tld, is shown in Figure 6.This approach is very clean and is easy to maintain, as the design ensures that the logic foreach report is in a single location.package com.ensemsys.twb.presentation.crystal;import com.crystaldecisions.report.web.viewer.CrystalReportViewer;importcom.crystaldecisions.reports.reportengineinterface.JPEReportSourceFactory;import com.crystaldecisions.sdk.occa.report.data.ConnectionInfo;import com.crystaldecisions.sdk.occa.report.data.ConnectionInfos;import com.crystaldecisions.sdk.occa.report.data.Fields;import com.crystaldecisions.sdk.occa.report.data.IConnectionInfo;import com.crystaldecisions.sdk.occa.report.lib.ReportSDKExceptionBase;import com.crystaldecisions.sdk.occa.report.reportsource.IReportSource;import com.crystaldecisions.sdk.occa.report.reportsource.IReportSourceFactory2;import com.ensemsys.twb.ApplicationProperties;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.TagSupport;import java.util.Locale;Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 9 of 13www.ensemble-systems.comFigure 5: SimpleReportViewerTag.java (continued)./*** Displays a Crystal report.*/public class SimpleReportViewerTag extends TagSupport{private Report report;public Report getReport(){return report;}public void setReport( Report report ){this.report = report;}public int doEndTag() throws JspException{CrystalReportViewer viewer = newViewer( report );try{IReportSourceFactory2 rptSrcFactory = new JPEReportSourceFactory();IReportSource reportSource =(IReportSource) rptSrcFactory.createReportSource(report.getReportFileName(), getLocale() );viewer.setReportSource( reportSource );viewer.setDatabaseLogonInfos( newDBConnectionInfos() );viewer.setEnableLogonPrompt( false );Fields fields = report.getFields();if ( fields != null ){viewer.setParameterFields( fields );viewer.setEnableParameterPrompt( false );}viewer.processHttpRequest((HttpServletRequest) pageContext.getRequest(),(HttpServletResponse) pageContext.getResponse(),pageContext.getServletConfig().getServletContext(),pageContext.getOut() );}catch ( ReportSDKExceptionBase e ){throw new JspException( e );}finally{if ( viewer != null ){viewer.dispose();}}return EVAL_PAGE;}Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 10 of 13www.ensemble-systems.comprivate Locale getLocale(){// generally you want to change this method// to return the specific locale that your J2EE// application is usingreturn pageContext.getRequest().getLocale();}private static ConnectionInfos newDBConnectionInfos(){ConnectionInfos infos = new ConnectionInfos();IConnectionInfo con = new ConnectionInfo();con.setUserName( ApplicationProperties.getJDBCUser() );con.setPassword( ApplicationProperties.getJDBCPassword() );infos.add( con );return infos;}private static CrystalReportViewer newViewer( Report report ){CrystalReportViewer viewer = new CrystalReportViewer();viewer.setName( report.getName() );// set the viewer formatting and behaviour options//viewer.setSeparatePages( false );//viewer.setBestFitPage( true );// ... and so onreturn viewer;}}Figure 5: SimpleReportViewerTag.java (continued).<?xml version="1.0" encoding="ISO-8859-1" ?><!DOCTYPE taglibPUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN""http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"><taglib><tlib-version>1.0</tlib-version><jsp-version>1.2</jsp-version><short-name>Crystal Viewer Tag Library</short-name><description>Display a Crystal Report with paramters</description><tag><name>viewer</name><tagclass>com.ensemsys.twb.presentation.crystal.SimpleReportViewerTag</tag-class><body-content>JSP</body-content><attribute><name>report</name><required>true</required><rtexprvalue>true</rtexprvalue></attribute></tag></taglib>Figure 6: simplereportviewer.tld - the TLD for the SimpleReportViewer tag extension.Integrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 11 of 13www.ensemble-systems.comSince TWB only allows project managers to view reports, TWBýs existing access controlmechanisms were sufficient for controlling access to reports. It is anticipated that a futureversion of TWB may require more fine-grained access control - for example, users may berestricted to seeing data about specific projects on a report. In this case, we would utilize theCrystal Enterprise security features for this level of access control.Evaluation of ImplementationOur report implementation, using a combination of Crystal Reports and the Crystal JavaReporting Component had several advantages and a few disadvantages when compared withour previous JSP implementation.The biggest advantage was the speed and simplicity of creating visually appealing reports. Animportant factor contributing to the speed of creating reports is that the Crystal Reports visualeditor makes it possible to design and edit reports without having to build and deploy TWB. Anadditional benefit is that the ease of creating and modifying reports means that the developerdoes not need to have expertise in J2EE, meaning that a larger pool of developers are availableto work with the reports.Figure 7: Example of a Crystal Report integrated with Task Workbench.The Crystal API made it very simple to integrate the reports into a web application. Using acustom JSP tag took this simplicity a step further, with very little code being required todisplay a report. Making changes to reports is now almost trivial. Huge time savings were alsoIntegrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 12 of 13www.ensemble-systems.comrealized by the fact that we did not need to write code to support requirements such asprinting, exporting reports, tuning queries, etc.A comparison of the implementation effort of the original approach using hand-coded JSPversus the equivalent implementation with Crystal Reports is shown in Table 1.As outlined in this document, one of the reasons we choose Crystal technology is that CrystalDecisions (recently acquired by Business Objects) offers a number of solutions to addressscalability issues, particularly in the Crystal Enterprise suite. The performance experiencedwhen using the Crystal Java Reporting Component was adequate for our current requirements,but under higher loads we will need to switch to a more full-featured Crystal solution. This is avery simple change, due to the way we have integrated Crystal Reports with TWB. Essentially,the only changes required would be to alter the implementation of the SimpleReportViewerTagto use a Crystal RAS (or Crystal Enterprise) report source, and to alter the path to the reportsthemselves, in the ReportFactory class.Task Original JSP Crystal ReportsOne-timeSetup/Familiarization(Assumes JSP/javaknowledge)3 days (JDBC, queries) 4 days (Product, Integration)Initial Report Design andImplementation4 days (input and outputpages)10 days (creating andoptimizing queries,organizing the data returnedto match the output format)2 days (input pagesleveragedexisting)1 day (Crystal ReportsDesigner)Make reports available as CSV 8 days (summary, createhierarchy CSVs)1 dayIntegration/Test/Maintenance 5 days (introduction of subprojects,project groups)1 dayTotal 30 days 9 daysTable 1: Comparison of implementation effort in TWB for hand coded JSP ýVs- CrystalReports.The main disadvantage of using Crystal Reports in TWB is that some reports are not easy to reuseacross different databases. Naturally, our development and QA environments use differentdatabases from the production environment, and it is important to be able to deploy theapplication using an arbitrary database type/schema that have the same tables but areotherwise different. The only solution available to us has been to create reports by hand foreach database.Crystal Reports connect to the database directly using SQL via JDBC. This allows reports toperform better, but the trade-off is that the report is dependent on a database schema that isautomatically generated and updated when the CMP entity beans are built. The developersresponsible for the CMP entity beans need to keep this in mind when they make any changes.Using the database directly in the context of an EJB application can lead to concurrency issues,so care must be taken to ensure that the EJB container is configured for non-exclusive access.Despite the drawbacks mentioned, using Crystal Reports has proved to be a much betterapproach to creating reports for TWB, because of the huge time savings at both, theIntegrating Crystal Reports with a J2EE ApplicationCopyright 2003, Ensemble Systems Inc. Page 13 of 13www.ensemble-systems.comimplementation and maintenance phases of development. In short, integrating Crystal Reportswith TWB has allowed us to produce a superior reporting capability with room for futurerequirement expansion - in a fraction of the time that it took to implement a hand-coded JSPsolution.SummaryThe Crystal Java Reporting Component successfully fulfilled our requirements for asophisticated Java reporting solution that was simple to integrate with a J2EE application andthat provides high quality reports which are easy to create. Our method of integration, using acustom JSP tag combined with a factory class, provided a very simple way to integrate CrystalReports with TWB. The main drawback of using Crystal Reports is the lack of portability ofindividual reports, but the magnitude of the productivity gains we experienced using CrystalReports far outweighed this.
页:
[1]