水晶报表

 

绪论
       这篇文章,我们讨论j2ee应用程序结合水晶报表的经验,这个例子叫做“Task Workbench (TWB)”。这篇文章的目的是帮助大家评估在j2ee应用程序中使用水晶报表的优点和缺点。
       TWB ( www.taskworkbench.com )是提供工程和每日运作的实时信息的工作管理工具。它是一个web应用程序,jsp作为表示层,EJBs处理商业逻辑,CMP实体bean作为持久层映射到一个oracle数据库。
       TWB帮助项目经理了解不同方法下项目花费时间和员工在每个项目上的消耗。当设计这个程序的时候,一个非常重要的需求就是提供给项目经理和项目管理人员一个高水平的项目成员和工程数据的视图。
图一: 显示一个员工的任务和每个任务花费的时间。
 
       最初,我们用jsp创建报表。但是这个方法被证实太耗费时间,特别是创建联合报表的时候。所以开始寻找一个java报表解决方案。我们的基本要求是这个方案能够快速配置,并且轻松生成和修改报表,还有要容易整合到j2ee程序中。
       我们选择水晶报表因为它完全满足我们的需求。它是一个成熟的、同类产品中处于领导地位的的工具。这是一个重要的考虑,因为我们希望我们的报表方案可以随着TWB 一起成长。

实现
       水晶报表被整合进 TWB 中有两个关键目的:第一是实现花费最小代价增加新报表和维护现有报表。第二个目的是转换到更复杂报表方案时,可以通过简单配置实现。
       Crystal Java Reporting Component 有一个 jsp 标签库,提供创建基本报表的简单方法。我们决定使用自己的自定义 jsp 标签库,来使用 Crystal Java Reporting Component 显示报表。 这个方法允许我们定制报表外观和报表参数,这样可以保持设计简单容易。
       jsp 上唯一并且必须的 java 代码是一个为了得到一个显示在页面上的报表的实例而去访问工厂 class java 方法。图二是一个可以请求报表的简单的 jsp 例子。 Form 传递必需的参数到 view_report.jsp (图三)。
 
 

<%@ 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 &quot;Submit&quot; to view
the 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>&nbsp;</td>
<td>
<input type="submit" name="submitBtn" value="Submit"/>
</td>
</tr>
</table>
</form>
</body>
</html>

图二: simple_banked_time_report.jsp
 

<%@ 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 Crystal
final String REPORT_NAME = "CrystalEventTarget";
String reportName = (String) session.getAttribute( REPORT_NAME );
// refresh might have done via a report action
if ( 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>
 

图三: view_report.jsp
 
报表工厂类创建一个报表实例,用 request 参数来确定报表类型。这种方法巧妙的把商业逻辑通过一个方法( method )装入单独报表。在 ReportFactory 子集中,工厂方法对于复合报表来说是一个最好的解决办法- ReportFactory 能够代表子集创建一个报表。
 

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 here
if ( reportType.equals( ReportType.BANKED_TIME_SIMPLE ) )
{
report = newSimpleBankedTimeReport( request );
}else
{
throw new InvalidReportTypeException( reportType );
}
return report;
}
 

图四: ReportFactory.java 基于 form 提交的参数创建一个实例
 

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 );
}
}
 

图四: ReportFactory.java
 

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 );
}
}
 

图四: ReportFactory.java
 
 
       SimpleReportViewerTag.java 呈现报表,这个类使用水晶报表的 API 来完成画出报表的工作。这个类非常简单――它创建一个 CrystalReportViewer 实例,设置数据库连接信息和报表需要的参数,最后显示报表。对应的 TLD simplereportviewer.tld ,在图六列出。
 

package com.ensemsys.twb.presentation.crystal;
import com.crystaldecisions.report.web.viewer.CrystalReportViewer;
import
com.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;
 

图五: SimpleReportViewerTag.java 被执行来显示一个报表。
                                                            

/**
* 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;
}
 

图五: SimpleReportViewerTag.java
 
 

private Locale getLocale()
{
// generally you want to change this method
// to return the specific locale that your J2EE
// application is using
return 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 on
return viewer;
}
}
 

图五: SimpleReportViewerTag.java
 
 

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//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>
 

图六: simplereportviewer.tld 
 
       应为 TWB 仅仅允许管理者查看报表, TWB 现有的接口控制系统控制对报表的访问。将来, TWB 可以要求更精细的访问控制-例如,用户可以在有限的条件下查看特定项目的报表。在这个案例中,为了到达这种访问标准,我们使用水晶报表的安全特性。
 
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值