<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>咖啡舞者</title>
    <description>一个普通大学毕业的应届生，请多多指教。</description>
    <link>http://07360591.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Java RMI 之 Introduction</title>
        <author>咖啡舞者</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://07360591.javaeye.com">咖啡舞者</a>&nbsp;
          链接：<a href="http://07360591.javaeye.com/blog/211576" style="color:red;">http://07360591.javaeye.com/blog/211576</a>&nbsp;
          发表时间: 2008年07月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp; <strong><span style="font-size: 24pt;">Java RMI</span></strong> <span style="font-size: 24pt;"><strong>之 Introduction</strong></span><strong></strong><br /><br /><br />&nbsp;&nbsp;&nbsp; 注：主要内容参考官方文档。同时已经有人把翻译过Sun上RMI的lesson，如果想学习技术实例，可以查看，参考资源给出了链接。<br /><strong><span style="font-size: 18pt;"><br />Overview<br /><br /></span></strong></p>
<div style="padding-right: 5px; padding-left: 4px; font-size: 13px; padding-bottom: 4px; width: 98%; padding-top: 4px; background-color: #eeeeee; border: #cccccc 1px solid;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
<a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter.com/</a><br />
<br />
--><span style="color: #000000;"><span style="color: #600000;">Java&nbsp;Remote&nbsp;Method&nbsp;Invocation&nbsp;(Java&nbsp;RMI)&nbsp;enables&nbsp;the&nbsp;programmer&nbsp;to&nbsp;create&nbsp;distributed&nbsp;Java&nbsp;technology-based<br />&nbsp;to&nbsp;Java&nbsp;technology-based&nbsp;applications,&nbsp;in&nbsp;which&nbsp;the&nbsp;methods&nbsp;of&nbsp;remote&nbsp;Java&nbsp;objects&nbsp;can&nbsp;be&nbsp;invoked&nbsp;from <br />other&nbsp;Java&nbsp;virtual&nbsp;machines,&nbsp;possibly&nbsp;on&nbsp;different&nbsp;hosts.&nbsp;RMI&nbsp;uses&nbsp;object&nbsp;serialization&nbsp;to&nbsp;marshal&nbsp;and <br />unmarshal&nbsp;parameters&nbsp;and&nbsp;does&nbsp;not&nbsp;truncate&nbsp;types,&nbsp;supporting&nbsp;true&nbsp;object-oriented&nbsp;polymorphism.</span></span></div>
<p><br />使用Java RMI，可以创建分布式的Java应用程序，远程的Java对象的方法可以被不同的主机中不同的Java虚拟机调用。RMI使用对象序列化传递参数，并且不会丢失类型，支持面向对象的多态性。<br /><br /><br /><br /><br /><strong><span style="font-size: 18pt;">Introduction<br /><br /></span></strong><strong><span style="font-size: 14pt;">1.1 Background(背景)</span></strong><strong><span style="font-size: 14pt;"><br /><br /></span></strong></p>
<div style="padding-right: 5px; padding-left: 4px; font-size: 13px; padding-bottom: 4px; width: 98%; padding-top: 4px; background-color: #eeeeee; border: #cccccc 1px solid;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
<a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter.com/</a><br />
<br />
--><span style="color: #000000;"><span style="color: #600000;">Distributed&nbsp;systems&nbsp;require&nbsp;that&nbsp;computations&nbsp;running&nbsp;in&nbsp;different&nbsp;address&nbsp;spaces,&nbsp;potentially&nbsp;on&nbsp;different<br />&nbsp;hosts,&nbsp;be&nbsp;able&nbsp;to&nbsp;communicate.&nbsp;For&nbsp;a&nbsp;basic&nbsp;communication&nbsp;mechanism,&nbsp;the&nbsp;JavaTM&nbsp;programming&nbsp;language <br />supports&nbsp;sockets,&nbsp;which&nbsp;are&nbsp;flexible&nbsp;and&nbsp;sufficient&nbsp;for&nbsp;general&nbsp;communication.&nbsp;However,&nbsp;sockets&nbsp;require&nbsp;the <br />client&nbsp;and&nbsp;server&nbsp;to&nbsp;engage&nbsp;in&nbsp;applications-level&nbsp;protocols&nbsp;to&nbsp;encode&nbsp;and&nbsp;decode&nbsp;messages&nbsp;for&nbsp;exchange,&nbsp;and the&nbsp;design&nbsp;of&nbsp;such&nbsp;protocols&nbsp;is&nbsp;cumbersome&nbsp;and&nbsp;can&nbsp;be&nbsp;error-prone.<br /><br />An&nbsp;alternative&nbsp;to&nbsp;sockets&nbsp;is&nbsp;Remote&nbsp;Procedure&nbsp;Call&nbsp;(RPC),&nbsp;which&nbsp;abstracts&nbsp;the&nbsp;communication&nbsp;interface&nbsp;to&nbsp;the<br />&nbsp;level&nbsp;of&nbsp;a&nbsp;procedure&nbsp;call.&nbsp;Instead&nbsp;of&nbsp;working&nbsp;directly&nbsp;with&nbsp;sockets,&nbsp;the&nbsp;programmer&nbsp;has&nbsp;the&nbsp;illusion&nbsp;of <br />calling&nbsp;a&nbsp;local&nbsp;procedure,&nbsp;when&nbsp;in&nbsp;fact&nbsp;the&nbsp;arguments&nbsp;of&nbsp;the&nbsp;call&nbsp;are&nbsp;packaged&nbsp;up&nbsp;and&nbsp;shipped&nbsp;off&nbsp;to&nbsp;the <br />remote&nbsp;target&nbsp;of&nbsp;the&nbsp;call.&nbsp;RPC&nbsp;systems&nbsp;encode&nbsp;arguments&nbsp;and&nbsp;return&nbsp;values&nbsp;using&nbsp;an&nbsp;external&nbsp;data <br />representation,&nbsp;such&nbsp;as&nbsp;XDR.<br /><br />RPC,&nbsp;however,&nbsp;does&nbsp;not&nbsp;translate&nbsp;well&nbsp;into&nbsp;distributed&nbsp;object&nbsp;systems,&nbsp;where&nbsp;communication&nbsp;between <br />program-level&nbsp;objects&nbsp;residing&nbsp;in&nbsp;different&nbsp;address&nbsp;spaces&nbsp;is&nbsp;needed.&nbsp;In&nbsp;order&nbsp;to&nbsp;match&nbsp;the&nbsp;semantics&nbsp;of <br />object&nbsp;invocation,&nbsp;distributed&nbsp;object&nbsp;systems&nbsp;require&nbsp;remote&nbsp;method&nbsp;invocation&nbsp;or&nbsp;RMI.&nbsp;In&nbsp;such&nbsp;systems,&nbsp;a <br />local&nbsp;surrogate&nbsp;(stub)&nbsp;object&nbsp;manages&nbsp;the&nbsp;invocation&nbsp;on&nbsp;a&nbsp;remote&nbsp;object.<br /><br />The&nbsp;Java&nbsp;platform's&nbsp;remote&nbsp;method&nbsp;invocation&nbsp;system&nbsp;described&nbsp;in&nbsp;this&nbsp;specification&nbsp;has&nbsp;been&nbsp;specifically <br />designed&nbsp;to&nbsp;operate&nbsp;in&nbsp;the&nbsp;Java&nbsp;application&nbsp;environment.&nbsp;The&nbsp;Java&nbsp;programming&nbsp;language's&nbsp;RMI&nbsp;system&nbsp;assumes the&nbsp;homogeneous&nbsp;environment&nbsp;of&nbsp;the&nbsp;Java&nbsp;virtual&nbsp;machine&nbsp;(JVM),&nbsp;and&nbsp;the&nbsp;system&nbsp;can&nbsp;therefore&nbsp;take&nbsp;advantage <br />of&nbsp;the&nbsp;Java&nbsp;platform's&nbsp;object&nbsp;model&nbsp;whenever&nbsp;possible.</span></span></div>
<p><br />分布式系统要求计算能不同的地址空间上，也就是说在不同的主机上，要能进行通讯，对于基本的Java通讯机制，Java支持Sokets，对于一般的通讯来说Sockets足够了，但是Sockets要求客户端和服务器端使用应用级别的协议交换消息，并且这个协议比较笨重。<br /><br />另外一个选择就是RCP，RCP就是针对远程调用级别抽象出一个通讯接口。而不是直接的使用sockets，参数的调用被包装起来，使用时看起来就像本地调用过程，RCP系统编码参数和返回值使用的是外部数据请求，比如XDR<br /><br />在分布式系统中，程序级别的对象必须存在不同的地址空间中，然而RCP不能很好的支持。为了匹配语法上的对象调用，分布式系统要求使用RMI，在这样一个系统中，一个代理（存根）对象管理一个远程对象的调用。<br /><br />规范中描述Java平台RMI系统被是为Java应用环境特别设计。Java RMI系统假定其拥有相似的JVM环境<br /><br /><strong><span style="font-size: 14pt;"><br /><br />1.2 System Goals(系统目标)<br /><br /></span></strong></p>
<div style="padding-right: 5px; padding-left: 4px; font-size: 13px; padding-bottom: 4px; width: 98%; padding-top: 4px; background-color: #eeeeee; border: #cccccc 1px solid;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
<a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter.com/</a><br />
<br />
--><span style="color: #000000;"><span style="color: #600000;">The&nbsp;goals&nbsp;for&nbsp;supporting&nbsp;distributed&nbsp;objects&nbsp;in&nbsp;the&nbsp;Java&nbsp;programming&nbsp;language&nbsp;are:<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Support&nbsp;seamless&nbsp;remote&nbsp;invocation&nbsp;on&nbsp;objects&nbsp;in&nbsp;different&nbsp;virtual&nbsp;machines<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Support&nbsp;callbacks&nbsp;from&nbsp;servers&nbsp;to&nbsp;applets<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Integrate&nbsp;the&nbsp;distributed&nbsp;object&nbsp;model&nbsp;into&nbsp;the&nbsp;Java&nbsp;programming&nbsp;language&nbsp;in&nbsp;a&nbsp;natural&nbsp;way&nbsp;while <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retaining&nbsp;most&nbsp;of&nbsp;the&nbsp;Java&nbsp;programming&nbsp;language's&nbsp;object&nbsp;semantics<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Make&nbsp;differences&nbsp;between&nbsp;the&nbsp;distributed&nbsp;object&nbsp;model&nbsp;and&nbsp;local&nbsp;Java&nbsp;platform's&nbsp;object&nbsp;model&nbsp;apparent<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Make&nbsp;writing&nbsp;reliable&nbsp;distributed&nbsp;applications&nbsp;as&nbsp;simple&nbsp;as&nbsp;possible<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Preserve&nbsp;the&nbsp;type-safety&nbsp;provided&nbsp;by&nbsp;the&nbsp;Java&nbsp;platform's&nbsp;runtime&nbsp;environment<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Support&nbsp;various&nbsp;reference&nbsp;semantics&nbsp;for&nbsp;remote&nbsp;objects;&nbsp;for&nbsp;example&nbsp;live&nbsp;(nonpersistent)&nbsp;references,<br />&nbsp; &nbsp; &nbsp; persistent&nbsp;references,&nbsp;and&nbsp;lazy&nbsp;activation<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Maintain&nbsp;the&nbsp;safe&nbsp;environment&nbsp;of&nbsp;the&nbsp;Java&nbsp;platform&nbsp;provided&nbsp;by&nbsp;security&nbsp;managers&nbsp;and&nbsp;class&nbsp;loaders </span><br /></span></div>
<p><br /><strong>在Java中支持分布式对象的目标如下：</strong><br /><br /><span style="color: #000000;">&nbsp;&nbsp;&nbsp; * </span>支持不同的虚拟机之间无缝的调用远程对象。<br />&nbsp;&nbsp;&nbsp; * 支持从服务器端回执给applets<br />&nbsp;&nbsp;&nbsp; * 保持大部分java对象原有的语义，以一种很自然的方式把分布式对象模型整合于Java语言<br />&nbsp;&nbsp;&nbsp; * 区分分布式对象模型和本地平台的对象模型<br />&nbsp;&nbsp;&nbsp; * 尽可能简单的构建可靠的分布式应用<br />&nbsp;&nbsp;&nbsp; * 有Java运行环境保护类型（Type）安全<br />&nbsp;&nbsp;&nbsp; * 远程对象支持各种语义上的引用（reference），比如：live references,持久引用和延迟激活。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用安全管理器（security managers）和类加载器（class loaders）保护java平台的环境安全。<br /><br /><br /><br /><br /><strong><span style="font-size: 18pt;"><span style="font-size: 24pt;"><span style="font-size: 18pt;">Java Distributed Object Model<br /><br /></span></span></span><span style="font-size: 14pt;">2.1 Distributed Object Applications<br /><br /></span></strong></p>
<div style="padding-right: 5px; padding-left: 4px; font-size: 13px; padding-bottom: 4px; width: 98%; padding-top: 4px; background-color: #eeeeee; border: #cccccc 1px solid;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
<a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter.com/</a><br />
<br />
--><span style="color: #000000;"><span style="color: #640000;">RMI&nbsp;applications&nbsp;are&nbsp;often&nbsp;comprised&nbsp;of&nbsp;two&nbsp;separate&nbsp;programs:&nbsp;a&nbsp;server&nbsp;and&nbsp;a&nbsp;client.&nbsp;A&nbsp;typical&nbsp;server <br />application&nbsp;creates&nbsp;a&nbsp;number&nbsp;of&nbsp;remote&nbsp;objects,&nbsp;makes&nbsp;references&nbsp;to&nbsp;those&nbsp;remote&nbsp;objects&nbsp;accessible,&nbsp;and <br />waits&nbsp;for&nbsp;clients&nbsp;to&nbsp;invoke&nbsp;methods&nbsp;on&nbsp;those&nbsp;remote&nbsp;objects.&nbsp;A&nbsp;typical&nbsp;client&nbsp;application&nbsp;gets&nbsp;a&nbsp;remote <br />reference&nbsp;to&nbsp;one&nbsp;or&nbsp;more&nbsp;remote&nbsp;objects&nbsp;in&nbsp;the&nbsp;server&nbsp;and&nbsp;then&nbsp;invokes&nbsp;methods&nbsp;on&nbsp;them.&nbsp;RMI&nbsp;provides&nbsp;the <br />mechanism&nbsp;by&nbsp;which&nbsp;the&nbsp;server&nbsp;and&nbsp;the&nbsp;client&nbsp;communicate&nbsp;and&nbsp;pass&nbsp;information&nbsp;back&nbsp;and&nbsp;forth.&nbsp;Such&nbsp;an <br />application&nbsp;is&nbsp;sometimes&nbsp;referred&nbsp;to&nbsp;as&nbsp;a&nbsp;distributed&nbsp;object&nbsp;application.<br /><br /></span></span><span style="color: #000000;"><span style="color: #640000;">Distributed&nbsp;object&nbsp;applications&nbsp;need&nbsp;to:<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Locate&nbsp;remote&nbsp;objects<br />Applications&nbsp;can&nbsp;use&nbsp;one&nbsp;of&nbsp;two&nbsp;mechanisms&nbsp;to&nbsp;obtain&nbsp;references&nbsp;to&nbsp;remote&nbsp;objects.&nbsp;An&nbsp;application&nbsp;can&nbsp;&nbsp;&nbsp; <br />register&nbsp;its&nbsp;remote&nbsp;objects&nbsp;with&nbsp;RMI's&nbsp;simple&nbsp;naming&nbsp;facility,&nbsp;the&nbsp;rmiregistry,&nbsp;or&nbsp;the&nbsp;application&nbsp;can&nbsp;pass<br />and&nbsp;return&nbsp;remote&nbsp;object&nbsp;references&nbsp;as&nbsp;part&nbsp;of&nbsp;its&nbsp;normal&nbsp;operation.<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Communicate&nbsp;with&nbsp;remote&nbsp;objects<br />Details&nbsp;of&nbsp;communication&nbsp;between&nbsp;remote&nbsp;objects&nbsp;are&nbsp;handled&nbsp;by&nbsp;RMI;&nbsp;to&nbsp;the&nbsp;programmer,&nbsp;remote&nbsp;communication<br />looks&nbsp;like&nbsp;a&nbsp;standard&nbsp;method&nbsp;invocation.&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Load&nbsp;class&nbsp;bytecodes&nbsp;for&nbsp;objects&nbsp;that&nbsp;are&nbsp;passed&nbsp;as&nbsp;parameters&nbsp;or&nbsp;return&nbsp;values<br />Because&nbsp;RMI&nbsp;allows&nbsp;a&nbsp;caller&nbsp;to&nbsp;pass&nbsp;objects&nbsp;to&nbsp;remote&nbsp;objects,&nbsp;RMI&nbsp;provides&nbsp;the&nbsp;necessary&nbsp;mechanisms&nbsp;for<br />loading&nbsp;an&nbsp;object's&nbsp;code&nbsp;as&nbsp;well&nbsp;as&nbsp;transmitting&nbsp;its&nbsp;data.</span></span><br /></div>
<p><br />RMI应用程序经常是由两个单独的程序组成：服务器端和客户端，<br />一个典型的服务器端应用创建一系列远程对象，并使其对象的引用可访问，等客户端来调用远程对象的方法。一个典型的客户端获取一个服务器端远程对象（一个或多个）的远程引用，然后调用他们的方法。RMI提供机制让服务器端和客户端进行通讯和来回传递信息。像这样一个应用就是分布式应用。<br /><br /><br /><br /><strong>分布式应用必须：<br /><br /></strong>&nbsp;&nbsp; * 查找远程对象<br />&nbsp;&nbsp;&nbsp; 应用程序下面两者之一的机制来获得远程对象的引用。一个应用程序可以使用RMI的简单命名在注册处注册它的远程对象，或者应用程序可以像普通操作一样传递和返回远程对象的引用。<br /><br />&nbsp;&nbsp; *与远程对象进行通讯、<br />&nbsp;&nbsp;&nbsp; 远程对象之间的通讯具体操作有RMI来提供，使用时只要当作正常的方法调用即可。<br /><br />&nbsp;&nbsp; * 加载对象的字节码使其可以作为参数或返回值传递。<br />&nbsp;&nbsp;&nbsp;&nbsp; 因为RMI允许调用者传递对象到远程对象，RMI提供必要的机制加载对象的代码和传输它数据。<br /><br /></p>
<div style="padding-right: 5px; padding-left: 4px; font-size: 13px; padding-bottom: 4px; width: 98%; padding-top: 4px; background-color: #eeeeee; border: #cccccc 1px solid;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
<a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter.com/</a><br />
<br />
--><span style="color: #000000;"><span style="color: #620000;">The&nbsp;illustration&nbsp;below&nbsp;depicts&nbsp;an&nbsp;RMI&nbsp;distributed&nbsp;application&nbsp;that&nbsp;uses&nbsp;the&nbsp;registry&nbsp;to&nbsp;obtain&nbsp;references&nbsp;to<br />&nbsp;a&nbsp;remote&nbsp;object.&nbsp;The&nbsp;server&nbsp;calls&nbsp;the&nbsp;registry&nbsp;to&nbsp;associate&nbsp;a&nbsp;name&nbsp;with&nbsp;a&nbsp;remote&nbsp;object.&nbsp;The&nbsp;client&nbsp;looks <br />up&nbsp;the&nbsp;remote&nbsp;object&nbsp;by&nbsp;its&nbsp;name&nbsp;in&nbsp;the&nbsp;server's&nbsp;registry&nbsp;and&nbsp;then&nbsp;invokes&nbsp;a&nbsp;method&nbsp;on&nbsp;it.&nbsp;The&nbsp;illustration also&nbsp;shows&nbsp;that&nbsp;the&nbsp;RMI&nbsp;system&nbsp;uses&nbsp;an&nbsp;existing&nbsp;web&nbsp;server&nbsp;to&nbsp;load&nbsp;bytecodes&nbsp;of&nbsp;classes&nbsp;written&nbsp;in&nbsp;the&nbsp;Java programming&nbsp;language,&nbsp;from&nbsp;server&nbsp;to&nbsp;client&nbsp;and&nbsp;from&nbsp;client&nbsp;to&nbsp;server,&nbsp;for&nbsp;objects&nbsp;when&nbsp;needed.&nbsp;RMI&nbsp;can&nbsp;load<br />&nbsp;class&nbsp;bytecodes&nbsp;using&nbsp;any&nbsp;URL&nbsp;protocol&nbsp;(e.g.,&nbsp;HTTP,&nbsp;FTP,&nbsp;file,&nbsp;etc.)&nbsp;that&nbsp;is&nbsp;supported&nbsp;by&nbsp;the&nbsp;Java&nbsp;platform.</span></span></div>
<p><br /><span style="color: #000000;">下面这幅图描述了一个RMI分布式应用程序，用户使用registry获得一个远程对象的引用，Server端调用registry使用名字和一个远程对象关联。客户端在Server的registry中查找远程对象的引用并调用其方法。同时这个图也描述了，在需要时，RMI系统使用已经存在的 web&nbsp;server在client端和Server端之间加载Java&nbsp;Class的字节码。RMI可以使用URL协议（如e.g.,&nbsp;HTTP, &nbsp;FTP,&nbsp;file,&nbsp;etc），这些Java平台都支持<br /><br /><br /><img src="http://java.sun.com/docs/books/tutorial/figures/rmi/rmi-2.gif" height="181" alt="" width="355" /><br /><br /><br /><br /></span><strong><span style="font-size: 14pt;"><span style="font-size: 18pt;"><span style="font-size: 14pt;">2.3 The Distributed and Nondistributed Models Contrasted<br /><br /></span></span></span></strong></p>
<div style="padding-right: 5px; padding-left: 4px; font-size: 13px; padding-bottom: 4px; width: 98%; padding-top: 4px; background-color: #eeeeee; border: #cccccc 1px solid;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
<a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter.com/</a><br />
<br />
--><span style="color: #000000;"><span style="color: #640000;">The&nbsp;Java&nbsp;SE&nbsp;platform's&nbsp;distributed&nbsp;object&nbsp;model&nbsp;differs&nbsp;from&nbsp;the&nbsp;Java&nbsp;SE&nbsp;platform's&nbsp;object&nbsp;model&nbsp;in&nbsp;these <br />ways:<br />*&nbsp;&nbsp;&nbsp;&nbsp;Clients&nbsp;of&nbsp;remote&nbsp;objects&nbsp;interact&nbsp;with&nbsp;remote&nbsp;interfaces,&nbsp;never&nbsp;with&nbsp;the&nbsp;implementation&nbsp;classes&nbsp;of <br />those&nbsp;interfaces.<br />*&nbsp;&nbsp;&nbsp;&nbsp;Non-remote&nbsp;arguments&nbsp;to,&nbsp;and&nbsp;results&nbsp;from,&nbsp;a&nbsp;remote&nbsp;method&nbsp;invocation&nbsp;are&nbsp;passed&nbsp;by&nbsp;copy&nbsp;rather&nbsp;than&nbsp;by<br />&nbsp;reference.&nbsp;This&nbsp;is&nbsp;because&nbsp;references&nbsp;to&nbsp;objects&nbsp;are&nbsp;only&nbsp;useful&nbsp;within&nbsp;a&nbsp;single&nbsp;virtual&nbsp;machine.<br />*&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;remote&nbsp;object&nbsp;is&nbsp;passed&nbsp;by&nbsp;reference,&nbsp;not&nbsp;by&nbsp;copying&nbsp;the&nbsp;actual&nbsp;remote&nbsp;implementation.<br />*&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;semantics&nbsp;of&nbsp;some&nbsp;of&nbsp;the&nbsp;methods&nbsp;defined&nbsp;by&nbsp;class&nbsp;java.lang.Object&nbsp;are&nbsp;specialized&nbsp;for&nbsp;remote <br />objects.<br />*&nbsp;&nbsp;&nbsp;&nbsp;Since&nbsp;the&nbsp;failure&nbsp;modes&nbsp;of&nbsp;invoking&nbsp;remote&nbsp;objects&nbsp;are&nbsp;inherently&nbsp;more&nbsp;complicated&nbsp;than&nbsp;the&nbsp;failure <br />modes&nbsp;of&nbsp;invoking&nbsp;local&nbsp;objects,&nbsp;clients&nbsp;must&nbsp;deal&nbsp;with&nbsp;additional&nbsp;exceptions&nbsp;that&nbsp;can&nbsp;occur&nbsp;during&nbsp;a&nbsp;remote<br />&nbsp;method&nbsp;invocation.</span></span></div>
<p><br />Java SE平台的分布式对象模型普通对象模型的区别：<br /><br />* 远程对象的Client端只与远程接口进行交互，不触及接口的实现。<br />* 不管是参数传递还是值返回，远程方法调用时传递的都是值的Copy而不是引用，因为一个对象的引用只有在单个虚拟机中有用。<br />* 远程对象传递的是引用，而不是copy实际的远程实现。<br />* Java.lang.Object为远程对象定义了一些专门的方法<br />* 调用远程对象天生就比调用本地方法，在失败mode上要复杂。所以客户端要处理远程调用中出现的额外的异常<br /><strong><br /><br /><br />参考资源：</strong><br />&nbsp;&nbsp;&nbsp;&nbsp; RMI lesson翻译: http://www.blogjava.net/jht/archive/2007/05/09/116216.html<br />&nbsp;&nbsp;&nbsp;&nbsp; 官方文档：http://java.sun.com/javase/6/docs/technotes/guides/rmi/index.html </p>
          <br/>
          <span style="color:red;">
            <a href="http://07360591.javaeye.com/blog/211576#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 04 Jul 2008 15:19:21 +0800</pubDate>
        <link>http://07360591.javaeye.com/blog/211576</link>
        <guid>http://07360591.javaeye.com/blog/211576</guid>
      </item>
      <item>
        <title>Java远程方法调用</title>
        <author>咖啡舞者</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://07360591.javaeye.com">咖啡舞者</a>&nbsp;
          链接：<a href="http://07360591.javaeye.com/blog/211561" style="color:red;">http://07360591.javaeye.com/blog/211561</a>&nbsp;
          发表时间: 2008年07月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="font-size: small;">来源:&nbsp; 网络</span></p>
<p><br /><span style="font-size: small;">概述 </span></p>
<p><span style="font-size: small;">　　Java Remote Method Invocation ( RMI -- Java远程方法调用)允许您使用Java编写分布式对象。本文将介绍RMI的优点以及如何将其连接到现有的和原有的系统中，以及与用Java 编写的组件的连接。 </span></p>
<p><span style="font-size: small;">　　RMI为采用Java对象的分布式计算提供了简单而直接的途径。这些对象可以是新的Java对象，也可以是围绕现有API的简单的Java包装程序。Java体现了&ldquo;编写一次就能在任何地方运行的模式。而RMI可将Java模式进行扩展，使之可在任何地方运行&rdquo;。 </span></p>
<p><span style="font-size: small;">　　因为RMI是以Java为核心的，所以，它将Java的安全性和可移植性等强大功能带给了分布式计算。您可将代理和梢务逻辑等属性移动到网络中最合适的地方。如果您要扩展Java在系统中的使用，RMI将使您充分利用其强大功能。 </span></p>
<p><span style="font-size: small;">　　RMI可利用标准Java本机方法接口JNI与现有的和原有的系统相连接。RMI还可利用标准JDBC包与现有的关系数据库连接。RMI/JNI和RMI/JDBC相结合，可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信，而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。 </span></p>
<p><span style="font-size: small;">优点 </span></p>
<p><span style="font-size: small;">　　从最基本的角度看，RMI是Java的远程过程调用(RPC)机制。与传统的RPC系统相比，RMI具有若干优点，因为它是Java面向对象方法的一部分。传统的RPC系统采用中性语言，所以是最普通的系统--它们不能提供所有可能的目标平台所具有的功能。 </span></p>
<p><span style="font-size: small;">　　RMI以Java为核心，可与采用本机方法与现有系统相连接。这就是说，RMI可采用自然、直接和功能全面的方式为您提供分布式计算技术，而这种技术可帮助您以不断递增和无缝的方式为整个系统添加Java功能。 </span></p>
<p><span style="font-size: small;">RMI的主要优点如下： </span></p>
<p><span style="font-size: small;">　面向对象：RMI可将完整的对象作为参数和返回值进行传递，而不仅仅是预定义的数据类型。也就是说，您可以将类似Java哈希表这样的复杂类型作为一个参数进行传递。而在目前的RPC系统中，您只能依靠客户机将此类对象分解成基本数据类型，然后传递这些数据类型，最后在服务器端重新创建哈希表。RMI则不需额外的客户程序代码(将对象分解成基本数据类型)，直接跨网传递对象。 <br />　可移动属性：RMI可将属性(类实现程序)从客户机移动到服务器，或者从服务器移到客户机。例如，您可以定义一个检查雇员开支报告的接口，以便察看雇员是否遵守了公司目前实行的政策。在开支报告创建后，客户机就会从服务器端获得实现该接口的对象。如果政策发生变化，服务器端就会开始返回使用了新政策的该接口的另一个实现程序。您不必在用户系统上安装任何新的软件就能在客户端检查限制条件--从而向用户提供烁快的反馈，并降低服务器的工作量。这样就能具备最大的灵活性，因为政策改变时只需要您编写一个新的Java类，并将其在服务器主机上安装一次即可。 <br />　设计方式：对象传递功能使您可以在分布式计算中充分利用面向对象技术的强大功能，如二层和三层结构系统。如果您能够传递属性，那么您就可以在您的解决方案中使用面向对象的设计方式。所有面向对象的设计方式无不依靠不同的属性来发挥功能，如果不能传递完整的对象--包括实现和类型--就会失去设计方式上所提供的优点。 <br />　安　　全：RMI使用Java内置的安全机制保证下载执行程序时用户系统的安全。RMI使用专门为保护系统免遭恶意小应用程序侵害而设计的安全管理程序，可保护您的系统和网络免遭潜在的恶意下载程序的破坏。在情况严重时，服务器可拒绝下载任何执行程序。 <br />便于编写和使用：RMI使得Java远程服务程序和访问这些服务程序的Java客户程序的编写工作变得轻松、简单。远程接口实际上就是Java接口。服务程序大约用三行指令宣布本身是服务程序，其它方面则与任何其它Java对象类似。这种简单方法便于快速编写完整的分布式对象系统的服务程序，并快速地制做软件的原型和早期版本，以便于进行测试和评估。因为RMI程序编写简单，所以维护也简单。 <br />　可连接现有/原有的系统：RMI可通过Java的本机方法接口JNI与现有系统进行进行交互。利用RMI和JNI，您就能用Java语言编写客户端程序，还能使用现有的服务器端程序。在使用RMI/JNI与现有服务器连接时，您可以有选择地用Java重新编写服务程序的任何部分，并使新的程序充分发挥Java的功能。类似地，RMI可利用JDBC、在不修改使用数据库的现有非Java源代码的前提下与现有关系数据库进行交互。 <br />　编写一次，到处运行：RMI是Java&ldquo;编写一次，到处运行 &rdquo;方法的一部分。任何基于RMI的系统均可100%地移植到任何Java虚拟机上，RMI/JDBC系统也不例外。如果使用RMI/JNI与现有系统进行交互工作,则采用JNI编写的代码可与任何Java虚拟机进行编译、运行。 <br />　分布式垃圾收集：RMI采用其分布式垃圾收集功能收集不再被网络中任何客户程序所引用的远程服务对象。与Java 虚拟机内部的垃圾收集类似，分布式垃圾收集功能允许用户根据自己的需要定义服务器对象，并且明确这些对象在不再被客户机引用时会被删除。 <br />　并行计算：RMI采用多线程处理方法，可使您的服务器利用这些Java线程更好地并行处理客户端的请求。Java分布式计算解决方案：RMI从JDK 1.1开始就是Java平台的核心部分，因此，它存在于任何一台1.1 Java虚拟机中。所有RMI系统均采用相同的公开协议，所以，所有Java 系统均可直接相互对话，而不必事先对协议进行转换。</span></p>
<p><span style="font-size: small;">传递属性</span></p>
<p><br /><span style="font-size: small;">　　前面我们讲到，RMI可以传递属性，并简单介绍了一下一个有关开支报告程序的情况。下面我们将深入讨论如何设计这样的系统。这样介绍的目的是使您能够利用RMI的功能将属性从一个系统传递到另一个系统，并随心所欲地安排当前的计算地点，并便于将来的改变。下面的例子并未涉及真实世界可能发生的所有问题，但可帮助读者了解处理问题的方法。 </span></p>
<p><span style="font-size: small;">服务器定义的策略 </span></p>
<p><span style="font-size: small;">　　图1是可进行动态配置的开支报告系统的示意图。客户机向用户显示图形用户界面(GUI)，用户填写开支报告。客户机程序使用RMI与服务器进行通信。服务器使用JDBC( Java关系数据库连接包)将开支报告存储在数据库中。至此，这看起来与其它多层次系统大同小异，但有一个重大区别-- RMI能下载属性。 <br />假定公司关于开支报告的政策发生改变。例如，目前公司只要求对超过20美元的开支需开具发票。但到明天，公司认为这太宽松了，便决定除不超过20美元的餐费以外，任何开支均需开具发票。如果不能下载属性的话，那么在设计便于修改的系统时您可选择下列方法之一： </span></p>
<p><span style="font-size: small;">　　用客户端安装与政策有关的程序。政策改变时，必须更新包含此政策的所有客户端程序。您可在若干服务器上安装客户程序，并要求所有用户从这些服务器之一运行客户程序，从而减少问题。但这仍不能彻底解决问题-- 那些让程序运行好几天的用户就无法使程序更新，而总是会有一些用户为了方便而把软件复制到本地磁盘上。 <br />　　您可要求服务器在每次向开支报告添加项目时检查政策。但这样就会在客户机和服务器之间产生大量数据流，并增加服务器的工作量。这还会使系统变得更加脆弱--网络故障会立即妨碍用户，而不仅仅是只在其呈交开支报告或启动新的报告时对其产生影响。同时，添加项目的速度也会变慢，因为这需要穿越整个网络往返一圈才能到达(不堪重负的)服务器。 <br />您可在呈交报告时要求服务器对政策进行检查。这样就会使用户创建很多必须待批报告的错误项目，而不是立刻捕捉到第一个错误，从而使用户有机会停止制造错误。为避免浪费时间，用户需要立刻得到有关错误的反馈。 <br />　　有了RMI，您就能以简单的方法调用程序从服务器得到属性，从而提供了一种灵活的方式，将计算任务从服务器卸载到客户机上，同时为用户提供速度更快的反馈。当用户准备编写一份新的开支报告时，客户机就会向服务器要求一个对象，该对象嵌入了适用于该开支报告的当前政策，就如同通过用Java编写的政策接口所表示的那样。该对象可以以任何方式实现当前政策。如果这是客户机RMI首次看到这种专门执行的政策，就会要求服务器提供一份执行过程的副本。如果执行过程将来发生变化，则一种新的政策对象将被返回给客户机，而RMI运行时则会要求得到新的执行过程。 <br />　　这表明，政策永远是动态的。您要想修改政策，就只需简单地编写通用政策接口的新的执行程序，把它安装在服务器上，并对服务器进行配置以返回这种新类型的对象即可。这样，每台客户机都会根据新的政策对新的开支报告进行检查。 </span></p>
<p><span style="font-size: small;">这是一种比任何静态方法都更好的方法，原因如下： </span></p>
<p><br /><span style="font-size: small;">　　所有客户机不必暂停或用新的软件来升级--软件可根据需要在不工作时加以更新。 <br />　　服务器不必参与项目检查工作，该工作可在本地完成。 <br />　　允许动态限制，因为对象执行程序(而不仅仅是数据)是在客户机和服务器之间进行传递的。 <br />　　使用户能立刻看到错误。 </span></p>
<p><br /><span style="font-size: small;">使客户机在服务器上所能调用的方法的远程接口定义如下： <br />import java.rmi.*; <br />public interface ExpenseServer extends Remote { <br />Policy getPolicy() throws RemoteException; <br />void submitReport(ExpenseReport report) <br />throws RemoteException, InvalidReportException; <br />}<br />import语句输入Java RMI包。所有RMI类型均在包java.rmi或其子包内定义。接口ExpenseServer是一般的Java接口，具有如下两种有趣的特点：</span></p>
<p><br /><span style="font-size: small;">它扩展了名为Remote的RMI接口，这使该接口标记为可用于远程调用。 </span></p>
<p><br /><span style="font-size: small;">它的所有方法均抛出RemoteException，后者用来表示网络或信息故障。</span></p>
<p><span style="font-size: small;">　　远程方法还可抛出您所需要的任何其他例外，但至少必须抛出RemoteException，这样您才能处理只会在分布式系统中发生的错误状态。该接口本身支持两个方法：getPolicy (返回一个实现政策接口的对象)，和submitReport (提交一个完成的开支请求，并在报告无论因何种原因使表格出现错误时，抛出一个例外)。 <br />政策接口本身可声明一种使客户机知道能否在开支报告中添加一个项目的方法： <br />public interface Policy { <br />void checkValid (Expenseentry entry) <br />throws PolicyViolationException; <br />}<br />如果该项目有效--即符合当前政策，则该方法可正常返回。否则就会抛出一个描述该错误的例外。政策接口是本地的(而非远程的)，所以将通过本机对象在客户机上执行--在客户机的虚拟机上，而非整个网络上运行的对象。客户机可能运行下列程序： <br />Policy curPolicy = server.getPolicy(); <br />start a new expense report <br />show the GUI to the user <br />while (user keeps adding entries) { <br />try { <br />curPolicy.checkValid(entry); // throws exception if not OK <br />add the entry to the expense report <br />} catch (PolicyViolationException e) { <br />show the error to the user <br />} <br />} <br />server.submitReport(report);</span></p>
<p><br /><span style="font-size: small;">　　当用户请求客户机软件启动一份新的开支报告时，客户机就调用server.getPolicy，并要求服务器返回一个包含当前开支政策的对象。添加的每个项目首先都被提交给该政策对象，以获得批准。如果政策对象报告无错误，则该项目就被添加到报告中；否则错误就被显示给用户，而后者可采取修正措施。当用户完成向报告中添加项目时，整个报告就被呈交。服务程序如下： <br />import java.rmi. *; <br />import java.rmi.server. *; <br />class ExpenseServerImpl <br />extends UnicastRemoteObject <br />implements ExpenseServer <br />{ <br />ExpenseServerImpl() throws RemoteException { <br />// . . . set up server state . . . <br />} <br />public Policy getPolicy() { <br />return new TodaysPolicy(); <br />} <br />public void submitReport(ExpenseReport report) { <br />// . . . write the report into the db . . . <br />} <br />}<br />除基本程序包外，我们还输入RMI的服务程序包。类型UnicastRemoteObject 定义了此服务程序远程对象的类型，在本例中，应为单一服务程序而非复制服务(下面还会详细介绍)。Java类ExpenseSeverImpl实现远程接ExpenseServer的方法。远程主机的客户机可使用RMI将信息发送给ExpenseServerImpl对象。</span></p>
<p><br /><span style="font-size: small;">本文中讨论的重要方法是getPolicy，它简单地返回定义当前政策的对象。下面看一个执行政策的例子： </span></p>
<p><span style="font-size: small;">public class TodaysPolicy implements Policy { <br />public void checkValid(ExpenseEntry entry) <br />throws PolicyViolationException <br />{ <br />if (entry.dollars() &lt; 20) { <br />return; // no receipt required <br />else if (entry.haveReceipt() == false) { <br />throw new PolicyViolationException; <br />} <br />} <br />}<br />TodaysPolicy进行检查的目的是确保无收据的任何项目均少于20美元。如果将来政策发生变化，仅少于20美元的餐费可不受&ldquo;需要收据&rdquo;政策的限制，则您可提供新的政策实现： <br />public class TomorrowsPolicy implements Policy { <br />public void checkValid(ExpenseEntry entry) <br />throws PolicyViolationException <br />{ <br />if (entry.isMeal() &amp;&amp; entry.dollars() &lt; 20) { <br />return; // no receipt required <br />} else if (entry.haveReceipt() == false) { <br />throw new PolicyViolationException; <br />} <br />} <br />}<br />　　编写这个类，并把它安装在服务器上，然后告诉服务器开始提供TomorrowsPolicy对象，而非daysPolicy对象，这样您的整个系统就会开始使用新的政策。当客户机调用服务器的getPolicy方法时，客户机的RMI就会检查返回的对象是否为已知类型。每台客户机首次遇到TomorrowsPolicy时，RMI就会在getPolicy返回前下载政策的实现。客户机可轻松地开始增强这个新的政策。</span></p>
<p><br /><span style="font-size: small;">　　RMI使用标准Java对象序列化机制传递对象。引用远程对象参数作为远程引用传递。如果向某方法提供的参数为原始类型或本机(非远程)对象，则向服务器传递一个深副本。返回值也拾照同样的方式处理，只不过是沿其它方向。RMI可使用户向本机对象传递和返回完整对象图并为远程对象传递和返回引用。 </span></p>
<p><span style="font-size: small;">　　在真实的系统中，getPolicy方法可能会有一个可以识别用户及开支报告类型(差旅、客户关系等)的参数，这样可使政策加以区别。您或者可以不要求单独的政策和开支报告对象，但您可以有一种newExpenseReport方法，它可返回一个直接检查政策的ExpenseReport对象。这最后一种策略可使您像修改政策一样简单地修改开支报告的内容--当公司决定需要把餐费划分为早餐、午餐和晚餐项目，而且像上述修改政策一样简单地执行修改时--可编写一个实现该报告的新类，客户程序就会自动使用这个类。</span></p>
<p><span style="font-size: small;">计算服务器 </span></p>
<p><span style="font-size: small;">　　开支报告的例子表示了客户机如何从服务器得到属性。属性可沿两个方向传递--客户机也可将新的类型传递给用户。最简单的例子就是如图2所示的计算服务器，该服务程序可执行任意任务，这样整个企业内的客户机都能利用高端或专用计算机。 </span></p>
<p><span style="font-size: small;">任务由一个简单的本地(非远程)接口定义： <br />public interface Task { <br />Object run(); <br />}<br />运行时，它就会进行一些计算，并返回一个包含结果的对象。这完全是一般性的任务--几乎所有计算任务都可在这个接口下实现。远程接口ComputeServer也同样简单： <br />import java.rmi.*; <br />public interface ComputeServer extends Remote { <br />Object compute(Task task) throws RemoteException; <br />}<br />这个远程接口的唯一目的就是使客户机创建一个Task (任务)对象，并把它发送给服务器执行，最后返回结果。该服务器的基本实现如下： <br />import java.rmi.*; <br />import java.rmi.server.*; <br />public class ComputeServerImpl <br />extends UnicastRemoteObject <br />implements ComputeServer <br />{ <br />public ComputeServerImpl() throws RemoteException { } <br />public Object compute(Task task) { <br />return task.run(); <br />} <br />public static void main(String[] args) throws Exception { <br />// use the default, restrictive security manager <br />System.setSecurityManager(new RMISecurityManager()); <br />ComputeServerImpl server = new ComputeServerImpl(); <br />Naming.rebind("ComputeServer", server); <br />System.out.println("Ready to receive tasks"); <br />return; <br />} <br />}</span></p>
<p><br /><span style="font-size: small;">　　如果您看一看compute方法就会发现，它非常简单。它只是接受传递给它的对象，并返回计算的结果。main方法包括服务器的启动代码--它安装了RMI的缺省安全管理程序，以防止他人存取本地系统，并创建可处理进入的请求的ComputeServerImpl对象，并将其与名字"ComputeServer"关联。这时，服务器已经准备好接收要执行的任务，而main 也完成了其设置。 <br />如上所述，这实际上是一种全面和实用的服务。系统可以得到改进，比如，可添加要计算的参数，从而对使用服务程序的部门进行计费。但在很多情况下，上述接口和及其实现允许使用高端计算机进行远程计算。这又明了RMI的简单性--如果您键入上述类，对其进行编译，并启动服务程序，您就拥有了能执行任意任务的运行计算服务器。 </span></p>
<p><span style="font-size: small;">　　下面介绍一个使用这种计算服务的例子。假定您购买了一个能运行大量计算操作应用程序的非常高端的系统。管理员可在该系统上启动一个Java虚拟机，运行ComputeServerImpl对象。该对象现在就可接受要运行的任务。 </span></p>
<p><span style="font-size: small;">现在假定一个小组准备通过一组数据培训一个神经网络，以帮助制订采购策略。他们可以采用的步骤如下： </span></p>
<p><span style="font-size: small;">　　定义一个类--暂且称之为PurchaseNet。它能接受一组数据，并运行培训数据，返回一个经过培训的神经网络。PurchaseNet 将实现Task (任务)接口，并在其run方法中执行其工作。他们可能还需要一个Neuron类来描述所返回的网络中的节点，而且很可能需要其它类来描述处理过程。run方法将返回一个由一组经过培训的Neuron对象组成的NeuralNet对象。 </span></p>
<p><br /><span style="font-size: small;">当这些类被编写好并进行小规模测试时，用一个PurchaseNet 对象调用ComputeServer的compute方法。 <br />当ComputeServerImpl对象中的RMI系统接收到作为进入参数的 PurchaseNet对象时，它就下载PurchaseNet的实现，并调用该服务器的compute方法，并把该对象作为Task (任务)参数。 <br />Task，也就是PurchaseNet对象，将开始执行其执行程序。当执行程序需要诸如Neuron和NeuralNet等新的类时，它们可根据需要下载。 <br />所有计算都将在计算服务器上执行，而客户机线程则等待结果。(客户机系统上的另一个线程则会显示&ldquo;正在等待&rdquo;光标或使用Java的内置并行机制执行另一个任务 )。当运行返回其NeuralNet对象时，这个对象将作为compute 方法的结果被传递回客户机。 <br />这不需要在服务器上安装其它软件--所有必须完成的任务都由各部门在其自己的计算机上完成，随后再根据需要传递给计算服务器主机。 <br />这个简单的计算服务器体系结构为系统的分布式功能提供了功能强大的转换能力。一项任务的计算可以被转移到一个能为其提供最好支持的系统上完成。这样的系统可以被用来： </span></p>
<p><span style="font-size: small;">　　在ComputeServerImpl对象运行于有数据挖掘需要的主机上，支持数据挖掘应用程序。这样可使您轻松地把任何计算移动到数据所在的地方。 <br />在从当前股票价格、发货信息或其它实时信息等外部资源获得直接数据的服务器上运行计算任务。 <br />通过运行ComputeServer (接受进入的请求并将其转送到运行 ComputeServerImpl的负担最小的服务器上)的不同实现，而将任务分布在多个服务器上。 <br />代理 <br />因为RMI允许使用Java实现下载属性，所以您可使用RMI 编写代理系统。代理的最简单格式如下： </span></p>
<p><span style="font-size: small;">import java.rmi.*; <br />public interface AgentServer extends Remote { <br />void accept(Agent agent) <br />throws RemoteException, InvalidAgentException; <br />} <br />public interface Agent extends java.io.Serializable { <br />void run(); <br />}<br />启动一个代理也就创建了实现Agent (代理)接口、找到服务器、激活接受该代理对象的类。该代理的执行程序将被下载到服务器上运行。accept方法将启动一个该代理的新线程，激活其run方法，然后返回，从而使该代理一直执行到run方法返回为止。代理可通过激活在另一台主机上运行的服务程序的accept方法而移植到该主机，而其本身则作为将被接受的代理来传递，并结束其在原来主机上的线程。 <br />面向对象的代码重用与设计模式 </span></p>
<p><span style="font-size: small;">　　面向对象的编程是一项允许代码重用的强大技术。很多企业组织都使用面向对象的编程来减轻创建程序的负担和提高系统的灵活性。RMI是完全面向对象的--信息被发送给远程对象，而且对象可以被传递和返回。 </span></p>
<p><span style="font-size: small;">Design Patterns (设计模式)目前在描述面向对象设计的实践活动中获得了相当大的成功。首先是因为Design Patterns 的创新工作而使之大受欢迎，这些编程方式是一种正式描述解决某类特定问题的完整方法的途径。所有这些设计模式都依赖于创建一个或多个抽象概念，这些抽象概念允许不同的实现，从而允许和增强软件重用。软件重用是面向对象技术的主要优势，而设计模式则是促进重用的最受欢迎的技术之一。 </span></p>
<p><span style="font-size: small;">所有设计模式都依赖面向对象的多态性--这是对象( 如Task )拥有多个实现的能力。算法的普通部分(如compute 方法)不必知道使用了哪个实现，它只需知道得到一个对象后应该对该对象采取什么操作。特别地，计算服务器就是Command (指令)模式的一个例子，它可使您将请求 (任务)表示为对象，并对其进行调度。 </span></p>
<p><span style="font-size: small;">只有当包括执行程序在内的完整对象能在客户机和服务器之间传递时，才会存在这样的多态性。DCE和DCOM等传统的RPC系统以及CORBA等基于对象的RPC系统不能下载并执行程序，因为它们不能把真实对象作为参数传递，而只能传递数据。 </span></p>
<p><span style="font-size: small;">　　RMI可传递包括执行程序在内的所有类型，所以您可以在分布式计算解决方案中，而不仅仅是本地计算中使用面向对象的编程--包括设计方式。如果没有RMI这样完全面向对象的系统，那么您就必须放弃很多分布式计算系统中的设计方式--以及面向对象的软件重用的其它形式。</span></p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://07360591.javaeye.com/blog/211561#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 04 Jul 2008 15:00:22 +0800</pubDate>
        <link>http://07360591.javaeye.com/blog/211561</link>
        <guid>http://07360591.javaeye.com/blog/211561</guid>
      </item>
      <item>
        <title>[原]BIRT 2.2.2 运行时使用已有的java.sql.Connection来生成报表</title>
        <author>咖啡舞者</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://07360591.javaeye.com">咖啡舞者</a>&nbsp;
          链接：<a href="http://07360591.javaeye.com/blog/201840" style="color:red;">http://07360591.javaeye.com/blog/201840</a>&nbsp;
          发表时间: 2008年06月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;注：本文实现的思路不是原创，思路原文来自：</p>
<p><a href="http://birtworld.blogspot.com/2005/12/using-supplied-connection-with-birt.html">http://birtworld.blogspot.com/2005/12/using-supplied-connection-with-birt.html</a></p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; 本文将介绍BIRT2.2 详细的扩展与配置：</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; 在ECLIPSE中新建一个工程 new-&gt;&nbsp;Bussiness Intelligence Report Tools&nbsp;-&gt; Web Project 名字：MyBirt</p>
<p>并设置好服务器信息（本文选用Tomcat5.5、JDK1.5）。</p>
<p>&nbsp;&nbsp; 建好工程以后，需要添家JDBC驱动程序到报表引擎中，本例连接ORACLE，因此将ORACLE的JDBC驱动包复制到 工程站点下的 /WEB-INF/platform/plugins/org.eclipse.birt.report.data.oda.jdbc_xxxxxxx/drivers目录下。</p>
<p>&nbsp;&nbsp; 接下来就是继承OdaJdbcDriver类了，再此之前需要在ClassPath中Add oda-jdbc.jar 和 org.eclipse.datatools.connectivity.oda_xxxxxxxxxxxx.jar这两个包，前者位于上边提到的drivers路径的上一级，后着则在plugins下。 新建一个Class：MyJdbcDriver，指定到某个包路径下，代码如下：</p>
<pre name="code" class="java">package com.wenmin.birt.data.oda.jdbc;

import org.eclipse.birt.report.data.oda.jdbc.OdaJdbcDriver;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Properties;

import org.eclipse.birt.report.data.oda.jdbc.*;
import org.eclipse.datatools.connectivity.oda.IConnection;
import org.eclipse.datatools.connectivity.oda.OdaException;

public class MyJdbcDriver extends OdaJdbcDriver 
{

	private Connection passedInConnection;
	public static final String DATASOURCE_KEY_CONNECTION = "com.wenmin.birt.data.odb.jdbc.MyJdbcDriver";
	
	public void setAppContext( Object context ) throws OdaException
	{
	    HashMap ctx = (HashMap)context;
	    passedInConnection = (java.sql.Connection)ctx.get(DATASOURCE_KEY_CONNECTION);
	
	}	
	
	public IConnection getConnection(String connectionClassName) throws OdaException
	{
		if( passedInConnection != null){
			return new appContextDBConnection();
		}else{
			return new org.eclipse.birt.report.data.oda.jdbc.Connection();
		}
	}
	


	private class appContextDBConnection extends org.eclipse.birt.report.data.oda.jdbc.Connection
	{
	
			
		public void open(Properties connProperties) throws OdaException
		{
			super.jdbcConn = passedInConnection;			
		
		}
		
		public void close( ) throws OdaException
		{
			if ( jdbcConn == null )
			{
				return;
			}
			//should have call to return connection to the pool
			jdbcConn = null;						
		}				
		
		
		
	}
}</pre>
<p>&nbsp;</p>
<p>OK，接下来我们来打个包，利用ECLIPSE EXPORT一个 jar包，（&ldquo;怎么做？&rdquo;，&ldquo;。。。拖下去，暴头！&rdquo;）。</p>
<p>本例导出包名字为：cst_driver.jar，拷贝到/WEB-INF/platform/plugins/org.eclipse.birt.report.data.oda.jdbc_xxxxxxx/目录下。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后就是修改配置文件，第一个需要改的文件plugin.xml位于 /WEB-INF/platform/plugins/org.eclipse.birt.report.data.oda.jdbc_xxxxxxx/目录下</p>
<p>找到如下节点：</p>
<pre name="code" class="xml">...
&lt;extension
         point="org.eclipse.datatools.connectivity.oda.dataSource"&gt;
      &lt;dataSource
            odaVersion="3.1"
            driverClass="org.eclipse.birt.report.data.oda.jdbc.OdaJdbcDriver"
            defaultDisplayName="%datasource.name"
            setThreadContextClassLoader="false"
            id="%oda.data.source.id"&gt;
....
</pre>
<p>&nbsp;</p>
<p>修改为：</p>
<pre name="code" class="xml">...
&lt;extension
         point="org.eclipse.datatools.connectivity.oda.dataSource"&gt;
      &lt;dataSource
            odaVersion="3.1"
            driverClass="com.wenmin.birt.data.oda.jdbc.MyJdbcDriver"
            defaultDisplayName="%datasource.name"
            setThreadContextClassLoader="false"
            id="%oda.data.source.id"&gt;
...</pre>
<p>&nbsp;其他内容不需要改变。</p>
<p>&nbsp;然后在META-INF下的MANIFEST.MF中找到如下位置，添加刚才导出的包cst_driver.jar</p>
<pre name="code" class="xml">Bundle-ClassPath: oda-jdbc.jar,derby.jar,cst_driver.jar</pre>
<p>&nbsp;</p>
<p>大功告成，接下来在程序中使用：</p>
<pre name="code" class="java">//....   
     HashMap contextMap = new HashMap();   </pre>
<pre name="code" class="java">    //...
     contextMap.put(MyJdbcDriver.DATASOURCE_KEY_CONNECTION, conn);  //conn为系统中获得的数据库连接
    task.setAppContext( contextMap );   //IRunAndRenderTask 
//...</pre>
<p>&nbsp;这样，无论在设计时指定了什么数据源，都会在运行时通过已有的conn连接数据库了。</p>
<p>&nbsp;由于是这两天才接触的BIRT，文章有不足之处还请多多指正，谢谢。</p>
          <br/>
          <span style="color:red;">
            <a href="http://07360591.javaeye.com/blog/201840#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 10 Jun 2008 16:49:21 +0800</pubDate>
        <link>http://07360591.javaeye.com/blog/201840</link>
        <guid>http://07360591.javaeye.com/blog/201840</guid>
      </item>
      <item>
        <title>BIRT Report Engine API</title>
        <author>咖啡舞者</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://07360591.javaeye.com">咖啡舞者</a>&nbsp;
          链接：<a href="http://07360591.javaeye.com/blog/200560" style="color:red;">http://07360591.javaeye.com/blog/200560</a>&nbsp;
          发表时间: 2008年06月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h1><a name="top"></a>Report Engine API</h1>
<blockquote>
<ul>
<li>Background </li>
<li>Installing the Engine </li>
<li>Configuring JDBC Drivers </li>
<li>API Overview </li>
<li>EngineConfig </li>
<li>ReportEngine </li>
<li>IReportRunnable </li>
<li>IReportDocument </li>
<li>IEngineTask </li>
<li>IDataExtractionTask </li>
<li>IGetParameterDefinitionTask </li>
<li>IRenderTask </li>
<li>IRunTask </li>
<li>IRunAndRenderTask </li>
<li>Miscellaneous </li>
<li>Emitter Configuration </li>
<li>BIRT Wiki RE API Examples </li>
</ul>
</blockquote>
<h2><a name="background"></a>Background</h2>
<p>The BIRT Report Engine API (RE API) allows you to integrate the run-time part of BIRT into your application. This may mean integrating into a stand alone Java application, deploying as part of a servlet or embedding it within an RCP application. The engine supports the following operations: </p>
<ul>
<li>Discover the set of parameters defined for a report. </li>
<li>Get the default values for parameters. </li>
<li>Run a report to produce HTML/Paginated HTML, WORD, XLS, PS, or PDF output. </li>
<li>Fetch an image or chart for a report. </li>
<li>Export Report data to CSV </li>
<li>Retrieve TOCs, Bookmarks, etc </li>
</ul>
<p>The report engine supports extensions for custom report items, data access, and custom output formats. Extensions are discussed elsewhere. This section will focus on how your application integrates with the report engine. </p>
<h2><a name="config"></a>Installing the Engine</h2>
<p>The first step is to download, install, and configure the BIRT engine. The engine is more than just a simple JAR file; it also includes a set of extensions (plug-ins), and a set of data drivers.</p>
<ul>
<li>First download the Report Engine from the <a href="http://download.eclipse.org/birt/downloads">Eclipse download site.</a> </li>
<li>This file will be named birt-runtime-version.zip. </li>
<li>Unzip this file into a directory, such as c:\birtruntime. </li>
<li>When you set the Engine Home, which is explained later, use C:/birtruntime/birt-runtime-version/ReportEngine as the value. </li>
<li>Add the jars in the ReportEngine/lib directory to your classpath/buildpath. </li>
</ul>
<p><br />Note that if you are using the RE API within an RCP/Eclipse application these steps are not needed. Just add the RE API plugins to your application and call the needed functions. </p>
<p>The BIRT Viewer sample is also bundled with the Report Engine download. It is located under the WebViewerExample directory. If you decide to use this example, see <a href="../viewerSetup.php">Viewer Setup</a> for an explanation. The Viewer uses the RE API to generate reports within the context of a servlet. It provides additional features, such as printing, TOCs, Page controls, etc. Before using the RE API, verify that the Viewer is not applicable to your application. </p>
<h2>Engine Source</h2>
<p>If you prefer to work directly with the BIRT source code, the Engine API is in the <code>org.eclipse.birt.report.engine project</code> within Eclipse CVS <a href="http://dev.eclipse.org/viewcvs/index.cgi/source/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/api/?cvsroot=BIRT_Project">Eclipse CVS</a>. The source is also available from the BIRT site in one package (the BIRT Source Code Package), which can be downloaded from the<a href="http://download.eclipse.org/birt/downloads"> download site.</a></p>
<h2>Javadoc</h2>
<p>This page provides an overview of the engine. To do actual development, consult the Engine Javadoc. These docs are available within BIRT Help.</p>
<h2><a name="jdbc"></a>Configuring JDBC Drivers</h2>
<p>You must configure the engine to include any JDBC drivers that you need.</p>
<p>To do this, copy the driver jar file to the ReportEngineInstall/birt-runtime-version/ReportEngine/plugins/org.eclipse.birt.report.data.oda.jdbc_version/drivers Directory. </p>
<h2><a name="api"></a>API Overview</h2>
<p>The following are the key steps to use the API, identifying the classes and interfaces you use for each step.</p>
<ol>
<li>Create an instance of <code>EngineConfig</code> to set options for the report engine. </li>
<li>Set the Engine Home and start the Platform (Loads the plug-ins). If you are using the RE API in plugin format there is no need to set BIRT Home. If you are using the RE API from within a servlet, be sure to read about PlatformServletContext later on this page. </li>
<li>Create an instance of the <code>ReportEngine</code> class. You can use this object to perform multiple tasks. </li>
<li>Open a report design using one of the <code>openReport(&nbsp;)</code> methods of <code>ReportEngine</code>. </li>
<li>Obtain information about report parameters using <code>IGetParameterDefinitionTask</code>. This is only needed if you want to build a custom parameter page or collect parameter definition information. The IRunTask and the IRunAndRenderTask interfaces have methods for setting parameter values. </li>
<li>Run and render a report using <code>IRunAndRenderReportTask</code> or <code>IRunTask</code> and <code>IRenderTask</code>. </li>
<li>Repeat steps 4-6 for the next report. </li>
<li>When done, call <code>shutdown(&nbsp;)</code> on your engine instance. If using the engine in a servlet shudown the engine when the servlet shuts down. </li>
</ol>
<p>The following sections describe the primary Engine classes in detail. The Engine API includes a number of secondary helper classes referenced within each description. The diagram below, serves as an overview of the classes needed to accomplish a given task. </p>
<p>&nbsp;</p>
<p><img src="http://www.eclipse.org/birt/phoenix/deploy/retask.jpg" height="600" alt="" style="vertical-align: middle;" width="800" /></p>
<p>&nbsp;</p>
<h2><a name="engineconfig"></a>EngineConfig</h2>
<p>Use the <code>EngineConfig</code> class to set global options for the report engine as a whole. Use it to specify the location of engine plug-ins, the location of data drivers, and to add application-wide scriptable objects.</p>
<pre>&gt;
EngineConfig config = new EngineConfig( );
config.setEngineHome( "put engine path here" );
</pre>
<p><br />The engine home should be set to installedlocation/birt-runtime-version/ReportEngine when deployed as a stand alone Java Application. See the <a href="http://wiki.eclipse.org/index.php/BIRT_Report_Engine_API">wiki</a> examples for Servlet and RCP deployment. </p>
<p>Other functions of interest within this class are: </p>
<ul>
<li><code>setLogConfig(String directoryName, Level level) </code><br />This call sets the Log directory name and level (OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST and ALL) <br />Setting directoryName to null will log to stdout. </li>
</ul>
<p>&nbsp;</p>
<h2><a name="reportengine"></a>ReportEngine</h2>
<p>The <code>ReportEngine</code>&nbsp;class represents the BIRT Report Engine. There is a significant cost associated with creating an engine instance, due primarily to the cost of loading extensions. Therefore, each application should create just one <code>ReportEngine</code> instance and use it to run multiple reports. This is important to remember when deploying the engine in a servlet as well. </p>
<p>The report engine is created through a factory supplied by the Platform. Before creating the engine, you should start the Platform, which will load the appropriate plug-ins. This is done by calling <code>Platform.startup(config)</code> that takes an <code>EngineConfig</code> object as argument. After using the engine, call <code>Plaform.shutdown()(&nbsp;)</code> function to do clean up work, which includes unloading the extensions.</p>
<pre>try{
	final config = new EngineConfig( );
	config.setEngineHome( "C:\birt-runtime-2_1_0\birt-runtime-2_1_0\ReportEngine" );
	config.setLogConfig(c:/temp, Level.FINE);
			
	Platform.startup( config );  //If using RE API in Eclipse/RCP application this is not needed.
	IReportEngineFactory factory = (IReportEngineFactory) Platform
			.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
	IReportEngine engine = factory.createReportEngine( config );
	engine.changeLogLevel( Level.WARNING );
			
}catch( Exception ex){
	ex.printStackTrace();
}
// Run reports, etc.
...

// destroy the engine.
try
{
	engine.destroy();
	Platform.shutdown();    
}
catch ( EngineException e1 )
{
    // Ignore
}</pre>
<p>Other functions of interest within this class are: </p>
<ul>
<li><code>engine.changeLogLevel(java.util.logging.Level.FINE); </code><br />Change engine log level (OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST and ALL) </li>
</ul>
<p>&nbsp;</p>
<h2><a name="ireportrunnable"></a>IReportRunnable</h2>
<p>BIRT report designs are stored as XML files. By default the extension is rptdesign. <br />To work with the report design in the engine, you must first load the report using one of the <code>openDesign(&nbsp;)</code> methods in the <code>ReportEngine</code> class.</p>
<p>The report design open methods return a <code>IReportRunnable</code> instance that represents the engine's view of the report design. </p>
<pre>IReportRunnable report = engine.openReportDesign( name );
</pre>
<p>Note that BIRT supplies many options for opening reports such as the filename or an input stream. Use the returned object to obtain parameter data or run the report.<br />The class provides methods for getting report properties such as the title, author and so on. It also provides methods for getting images embedded (stored) within the report design. If your application requires more information about the design, you can obtain a Design Engine report handle, then use the BIRT <a href="../designEngineAPI.php">Design Engine API</a> to traverse the report design.</p>
<h2><a name="ireportdocument"></a>IReportDocument</h2>
<p>BIRT optionally can store reports in an intermediate format, after generation and before rendering. This document, with the default extension rptdocument, can be manipulated with the <code>IReportDocument</code> interface. The Engine will create this document when the <code>runTask</code> is used. The BIRT viewer uses this format to do pagination, TOCs, CSV extraction, bookmarks, etc. When finished with a IReportDocument, be sure to close it, with the close() method. </p>
<p>The Engine's <code>openDocument</code> method returns a <code>IReportDocument</code> that represents the intermediate report document. </p>
<p>The example below illustrates getting the TOC from the report document, after the runTask is used to generate the report. </p>
<pre>IReportDocument ird = engine.openReportDocument("c:/work/test/TOCTest.rptdocument");
//get root node
TOCNode td = ird.findTOC(null);
List children = td.getChildren( );
//Loop through Top Level Children
if ( children != null &amp;&amp; children.size( ) &gt; 0 )
{
	for ( int i = 0; i &lt; children.size( ); i++ )
	{
		TOCNode child = ( TOCNode ) children.get( i );
		System.out.println( "Node ID " + child.getNodeID());
		System.out.println( "Node Display String " + child.getDisplayString());
		System.out.println( "Node Bookmark " + child.getBookmark());
			
	}
}
</pre>
<h2><a name="ienginetask"></a>IEngineTask</h2>
<p>BIRT reports support scripting. Operations that execute scripts require a scripting context. Report operations also require a locale. BIRT Engine tasks provide the framework for managing the scripting context, report locales and so on. In general, if an operation requires neither a script context nor a locale, it will appear as a method on the engine or the report design. However, if the operation does require these items, then a task class represents the operation.</p>
<p>For example, opening a design file or retrieving an image in the design file does not require setting up a scripting context. Other operations, such as retrieving default parameters, retrieving a dynamic selection list, and running and rendering a report, all support scripting, require a scripting context, and are represented as tasks.</p>
<p>Create tasks using the factory methods on the <code>ReportEngine</code> class. The supported Tasks are shown below:</p>
<ul>
<li><code>engine.createDataExtractionTask(); </code></li>
<li><code>engine.createGetParameterDefinitionTask(); </code></li>
<li><code>engine.createRenderTask(); </code></li>
<li><code>engine.createRunTask(); </code></li>
<li><code>engine.createRunAndRenderTask(); </code></li>
</ul>
<p>&nbsp;</p>
<h2><a name="idataextractiontask"></a>IDataExtractionTask</h2>
<p>Use this task to extract data from a report document. The BIRT viewer uses this class to extract report data into CSV format. This class supports extracting data from the report document by specifying the result set and columns you would like to have extracted. <br />From the Viewer code</p>
<pre>dataTask.selectResultSet( resultSetName );
dataTask.selectColumns( columnNames );
dataTask.setLocale( locale );
</pre>
<p>&nbsp;</p>
<p>Below is an example that uses the Data Extraction Task to extract the first two columns of data. </p>
<pre>//Open previously created report document
IReportDocument iReportDocument = engine.openReportDocument("c:/work/test/TOCTest.rptdocument");

//Create Data Extraction Task		
IDataExtractionTask iDataExtract = engine.createDataExtractionTask(iReportDocument);

//Get list of result sets		
ArrayList resultSetList = (ArrayList)iDataExtract.getResultSetList( );

//Choose first result set
IResultSetItem resultItem = (IResultSetItem)resultSetList.get( 0 );
String dispName = resultItem.getResultSetName( );
iDataExtract.selectResultSet( dispName );

IExtractionResults iExtractResults = iDataExtract.extract();
IDataIterator iData = null;
try{
	if ( iExtractResults != null )
	{
		iData = iExtractResults.nextResultIterator( );

		//iterate through the results
		if ( iData != null  ){
			while ( iData.next( ) )
			{	
				Object objColumn1;
			    Object objColumn2;
				try{
					objColumn1 = iData.getValue(0);
				}catch(DataException e){
					objColumn1 = new String("");
				}
				try{
					objColumn2 = iData.getValue(1);
				}catch(DataException e){
					objColumn2 = new String("");
				}
					System.out.println( objColumn1 + " , " + objColumn2 );
			}
			iData.close();
		}
	}
}catch( Exception e){
		e.printStackTrace();
}

iDataExtract.close();
</pre>
<h2><a name="igetparameterdefinitiontask"></a>IGetParameterDefinitionTask</h2>
<p>Use this task to obtain information about parameters. Parameter default values are expressions, and so a scripting context (represented by the task) is required. Parameter definitions provide access to the parameter definition information that the report designer entered at design time. If a parameter has custom XML or user-defined properties defined, then these are also available. Parameters can be organized into groups. Your application has the choice of retrieving the parameters organized by group (as they should be displayed to the user), or in ungrouped form (useful for creating a programmatic interface.)</p>
<p>The <code>IParameterGroupDefn</code> and <code>IScalarParameterDefn</code> interfaces provide information about parameter groups and individual parameters.</p>
<p>The following example opens a report design and iterates through the parameters and parameter groups. If a List Box parameter is found, which is not in a group, the selection values are retrieved. </p>
<pre>//Open a report design 
IReportRunnable design = engine.openReportDesign("C:/work/test/parameters.rptdesign"); 
		
IGetParameterDefinitionTask task = engine.createGetParameterDefinitionTask( design );
Collection params = task.getParameterDefns( true );

Iterator iter = params.iterator( );
//Iterate over all parameters
while ( iter.hasNext( ) )
{
	IParameterDefnBase param = (IParameterDefnBase) iter.next( );
	//Group section found
	if ( param instanceof IParameterGroupDefn )
	{
		//Get Group Name
		IParameterGroupDefn group = (IParameterGroupDefn) param;
		System.out.println( "Parameter Group: " + group.getName( ) );
		
		//Get the parameters within a group
		Iterator i2 = group.getContents( ).iterator( );
		while ( i2.hasNext( ) )
		{
			IScalarParameterDefn scalar = (IScalarParameterDefn) i2.next( );
			System.out.println("	" + scalar.getName());
		}
		        
	}
	else
	{
		//Parameters are not in a group
		IScalarParameterDefn scalar = (IScalarParameterDefn) param;
		System.out.println(param.getName());
		
		//Parameter is a List Box
		if(scalar.getControlType() ==  IScalarParameterDefn.LIST_BOX)
		{
		    Collection selectionList = task.getSelectionList( param.getName() );
		    //Selection contains data    
			if ( selectionList != null )
			{
				for ( Iterator sliter = selectionList.iterator( ); sliter.hasNext( ); )
				{
					//Print out the selection choices
					IParameterSelectionChoice selectionItem = (IParameterSelectionChoice) sliter.next( );
					String value = (String)selectionItem.getValue( );
					String label = selectionItem.getLabel( );
					System.out.println( label + "--" + value);
				}
			}		        
		}   
	}
}
		
task.close();
</pre>
<p>Use the <code>IGetParameterDefinitionTask</code> class to evaluate the default value for a parameter. The parameter default value is an expression, and the task provides the required execution context.</p>
<pre>IScalarParameterDefn param = ...;
IGetParameterDefinitionTask task = ...;
Object value = task.getDefaultValue( param );</pre>
<h2><a name="irendertask"></a>IRenderTask</h2>
<p>Use this task to render a report document to a specific output (eg, HTML, PDF, ...). This task expects the document to exist, which means it has been generated with the RunTask engine task. This class renders the report, based on the supplied page range, page number or all if no page is specified.</p>
<p>The following example renders the first two pages of the "Pages" report document. You will notice that it renders the two pages as one page of HTML. </p>
<pre>//Open a report document 
IReportDocument iReportDocument = engine.openReportDocument("c:/work/test/Pages.rptdocument");
//Create Render Task
IRenderTask task = engine.createRenderTask(iReportDocument);
//Set parent classloader report engine
task.getAppContext().put(EngineConstants.APPCONTEXT_CLASSLOADER_KEY, RenderTaskExample.class.getClassLoader()); 

IRenderOption options = new RenderOption();		
options.setOutputFormat("html");
options.setOutputFileName("output/resample/eventorder.html");
	
			
if( options.getOutputFormat().equalsIgnoreCase("html")){
	HTMLRenderOption htmlOptions = new HTMLRenderOption( options);
	htmlOptions.setImageDirectory("output/image");
	htmlOptions.setHtmlPagination(false);
	//set this if you want your image source url to be altered
	//If using the setBaseImageURL, make sure to set image handler to HTMLServerImageHandler
	htmlOptions.setBaseImageURL("http://myhost/prependme?image=");
			
	htmlOptions.setHtmlRtLFlag(false);
	htmlOptions.setEmbeddable(false);
}else if( options.getOutputFormat().equalsIgnoreCase("pdf") ){
			
	PDFRenderOption pdfOptions = new PDFRenderOption( options );
	pdfOptions.setOption( IPDFRenderOption.FIT_TO_PAGE, new Boolean(true) );
	pdfOptions.setOption( IPDFRenderOption.PAGEBREAK_PAGINATION_ONLY, new Boolean(true) );

}
//Use this method if you want to provide your own action handler
options.setActionHandler(new MyActionHandler());

//file based images
//options.setImageHandler(new HTMLCompleteImageHandler())
//Web based images.  Allows setBaseImageURL to prepend to img src tag
options.setImageHandler(new HTMLServerImageHandler());
		
IRenderTask task = engine.createRenderTask(document); 		
task.setRenderOption(options);
task.setPageRange("1-2");
task.render();
iReportDocument.close();

</pre>
<h2><a name="iruntask"></a>IRunTask</h2>
<p>Use this task to run a report and generate a report document, which is saved to disk. The report document can then be used with the <code>IRenderTask</code> to support features such as paging.</p>
<p>The following example simply creates a report document and saves it to disk.</p>
<pre>//Open a report design 
IReportRunnable design = engine.openReportDesign("C:/work/test/MyOrders.rptdesign"); 
		
//Create task to run the report - use the task to execute the report and save to disk.
IRunTask task = engine.createRunTask(design); 
//Set parent classloader for engine
task.getAppContext().put(EngineConstants.APPCONTEXT_CLASSLOADER_KEY, RunTaskExample.class.getClassLoader()); 
					
//run the report and destroy the engine
task.run("c:/work/test/MyOrders.rptdocument");		

task.close();
</pre>
<h2><a name="irunandrendertask"></a>IRunAndRenderTask</h2>
<p>Use this task to run a report and output it to one of the supported output formats. This task does not create a report document. Create a new task for each report that you run.</p>
<p>Reports may take parameters. The <code>IRunAndRenderReportTask</code> takes parameter values as a <code>HashMap</code> or you can set them individually. The <code>IRunAndRenderReportTask</code> provides the <code>validateParameters(&nbsp;)</code> method to validate the parameter values before you run the report.</p>
<pre>//Open the report design
IReportRunnable design = engine.openReportDesign("Reports/TopNPercent.rptdesign"); 
	

//Create task to run and render the report,
IRunAndRenderTask task = engine.createRunAndRenderTask(design); 
//Set parent classloader for engine
task.getAppContext().put(EngineConstants.APPCONTEXT_CLASSLOADER_KEY, RunAndRenderTaskExample.class.getClassLoader()); 
		
//Set parameter values and validate
task.setParameterValue("Top Percentage", (new Integer(3)));;
task.setParameterValue("Top Count", (new Integer(5)));
task.validateParameters();
				
//Setup rendering to HTML
HTMLRenderOption options = new HTMLRenderOption();		
options.setOutputFileName("output/resample/TopNPercent.html");
options.setOutputFormat("html");
//Setting this to true removes html and body tags
options.setEmbeddable(false);
		
task.setRenderOption(options);
//run and render report
task.run();
task.close();

</pre>
<h2><a name="miscellaneous"></a>Miscellaneous</h2>
<p>Described below are some miscellaneous items that bear mentioning when using the BIRT Report Engine API</p>
<h4>Adding Script Objects</h4>
<p>BIRT's script engine supports adding Java objects at the API level. If you wish the Java object to be available to the Report, retrieve the application context at the EngineConfig or Task level and add the object to the Map. For example, adding a Java object to the task. </p>
<p>Add the following code to your task. </p>
<pre>MyJavaObject jo = new MyJavaObject();
task.getAppContext().put("MyJavaScriptItem", jo);
</pre>
<p>Now within the the script editor, you can reference your Java Object as follows: </p>
<pre>testMyJavaObject = MyJavaScriptItem.getMyMethod();
</pre>
<h4>Rendering to an Output Stream</h4>
<p>When rendering a report it may be desirable to output the report to an output stream, such as <code>HttpServletResponse</code>. To do this modify your RenderOption instance to use <code>setOutputStream</code> instead of <code>setOutputFileName</code>. For example, </p>
<pre>public void webReport( HttpServletResponse response )
{
.
.
.
//Set rendering options 
HTMLRenderOption options = new HTMLRenderOption();
options.setOutputStream(response);
task.setRenderOption(options);		
//run the report
task.run();
}
</pre>
<h4>IPlatformContext - Web Based Plugin Loading</h4>
<p>By default BIRT loads plug-ins located in the BIRT_HOME/plugins directory. The plug-ins loaded provide functionality for connecting to data sources, emitters (eg, PDF, WORD, XLS, HTML ...), and chart rendering. BIRT_HOME in the examples on this page is set using the setEngineHome method of the <code>EngineConfig</code> class. BIRT loads these plug-ins using the Java File API. </p>
<p>This method is usually sufficient. If deployed to a web application, the developer can usually call ServletContext.getRealPath to retrieve the real path and set the BIRT_HOME accordingly. This can present a problem when deploying to a war file. Certain application servers will return null when getRealPath is called. This will result in the plug-ins not getting loaded. </p>
<p>The IPlatformContext interface describes the methods needed to load the resources required by the BIRT runtime. Within BIRT there are two implementations of this interface, PlatformFileContext() and PlatformServletContext(). The Platform Context is set using the setEngineContext method of the <code>EngineConfig</code> class. If this method is not called it defaults to PlaformFileContext() and uses the Java File API to load the resources. The <code>PlatformServletContext</code> class uses Resource based operations. So if you are deploying an application to the Web that uses the BIRT API and it is not contained in a war you can use the default and set your engine home to something similar to: </p>
<pre>config.setEngineHome( servletContext.getRealPath("/WEB-INF"));
</pre>
<p>The BIRT Viewer application will load the plug-ins from /WEB-INF/Plugins directory. If you deploy your application in a war, setup your code like: </p>
<pre>//this causes the plug-in loader to look in the current directory.
config.setEngineHome("");
//Using the PlatformServletContext will cause the OSGi loader to look for the
//plug-ins in the WEB-INF/Platform directory.  If this directory
//does not exist create it. 
//Next copy the plug-ins directory from the ReportEngine directory to
//the WEB-INF/Platform/ directory.
IPlatformContext context = new PlatformServletContext( sc );
config.setPlatformContext( context );



</pre>
<p>&nbsp;</p>
<h4>Embedding Report Output</h4>
<p>If you need to embed your report output into an existing web page you can use the <code>options.setEmbeddable(true);</code> method. This will remove the &lt;HTML&gt; and &lt;BODY&gt; tags in the generated report. For example, </p>
<pre>public void embedReport( HttpServletResponse response )
{
.
.
.
//Set rendering options 
HTMLRenderOption options = new HTMLRenderOption();
options.setOutputStream(response);
options.setEmbeddable(true);
task.setRenderOption(options);		
//run the report
task.run();
}
</pre>
<h2><a name="emitterconfiguration"></a>Emitter Configuration</h2>
<p>An "emitter" is the component of the report engine that produces output. BIRT provides many emitters such as HTML, XLS and PDF. To configure emitter options you need to use the RenderOption class. In some of the examples above this was done, with code similar to the following: </p>
<pre>IRenderOption options = new RenderOption();		
options.setOutputFormat("html");
options.setOutputFileName("output/resample/eventorder.html");
</pre>
<p>BIRT also provides an extended RenderOption class for the PDF and HTML emitters (PDFRenderOption and HTMLRenderOption). The PDFRenderOption class provides methods for handling fonts and the HTMLRenderOption class provides methods for handling images, url encoding, and other html specific settings. You may need to configure the HTML emitter to manage images.<br />BIRT supports several image types: </p>
<p>&nbsp;</p>
<ul>
<li>Images referenced using a URL </li>
<li>Embedded images </li>
<li>Images created to represent charts </li>
<li>Images from BLOB types </li>
</ul>
<p>Your application must have a policy for handling images in HTML. URL-based images are usually no problem. However, you'll need to handle the others. The <code>IHTMLImageHandler</code> interface defines the policy. The BIRT Engine provides two default implementations:</p>
<ul>
<li><code>HTMLCompleteImageHandler</code>: used to write images to disk when rendering a report to produce an HTML file on disk. </li>
<li><code>HTMLServerImageHandler</code>: used to handle images for an engine running in an app server. This class is used by the BIRT example web viewer. </li>
</ul>
<p>When you instantiate a <code>EngineConfig</code> class the <code>HTMLCompleteImageHandler</code> class is used by default. Images will be created in your temporary files location (ie C:\Documents and Settings\User\Local Settings\Temp). If this is not desired you can use the <code>HTMLRenderOption</code> class to change this location. </p>
<pre>HTMLRenderOption htmlOptions = new HTMLRenderOption( options);
htmlOptions.setImageDirectory("output/image");
		
   </pre>
<p>Setting the Image Directory instructs the RE API to store images, used within the report output, in that directory. If your emitter is setup with the standard <code>HTMLCompleteImageHandler</code>, the output image src attribute will point to this directory using the File protocol. If your emitter is configured with the <code>HTMLServerImageHandler</code>, you can specify an image base URL using the <code>HTMLRenderOption</code> class. The engine will prepend all images with this URL in the src attribute of the report output. </p>
<pre>HTMLRenderOption options = new HTMLRenderOption();		
options.setOutputFileName("output/resample/TopNPercent.html");
options.setOutputFormat("html");
options.setImageDirectory("C:\apps\apache-tomcat-5.5.20\webapps\2.2\images");
options.setBaseImageURL("http://localhost:8080/2.2/images/");		
options.setImageHandler(new HTMLServerImageHandler());
task.setRenderOption(options);
</pre>
<p>You can also create your own implementation of <code>IHTMLImageHandler</code> if the above don't meet your needs.</p>
<p>If your code is going to be run in a servlet, please review the ReportEngineService.java code in CVS <a href="http://dev.eclipse.org/viewcvs/index.cgi/source/org.eclipse.birt.report.viewer/birt/WEB-INF/classes/org/eclipse/birt/report/services/ReportEngineService.java?rev=HEAD&amp;cvsroot=BIRT_Project&amp;content-type=text/vnd.viewcvs-markup">ReportEngineService.java</a> The BIRT Viewer uses this class to configure the engine. The constructor for the ReportEngineService uses the <code>HTMLServerImageHandler</code> class, which in turn reads Image locations from the web.xml file. </p>
          <br/>
          <span style="color:red;">
            <a href="http://07360591.javaeye.com/blog/200560#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 05 Jun 2008 16:20:40 +0800</pubDate>
        <link>http://07360591.javaeye.com/blog/200560</link>
        <guid>http://07360591.javaeye.com/blog/200560</guid>
      </item>
      <item>
        <title>BIRT部署并利用API生成PDF</title>
        <author>咖啡舞者</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://07360591.javaeye.com">咖啡舞者</a>&nbsp;
          链接：<a href="http://07360591.javaeye.com/blog/200507" style="color:red;">http://07360591.javaeye.com/blog/200507</a>&nbsp;
          发表时间: 2008年06月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>注：本文转自 <a href="http://blog.csdn.net/xujiaqiang/archive/2008/01/19/2052789.aspx">http://blog.csdn.net/xujiaqiang/archive/2008/01/19/2052789.aspx</a></strong></p>
<p><strong>感谢作者分享。</strong></p>
<p><strong></strong></p>
<p>&nbsp;</p>
<p><strong>Birt报表设计步骤：</strong></p>
<p>1、下载birt all in one 2.2.1.1，包含eclipse，解开下载文件<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 启动eclipse，<strong>必须使用JDK1.5启动eclipse</strong>，否则新建工程对话框中没有birt的相关选项</p>
<p><br />2、新建report project和report<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 打开data explorer视图，新建一个data sources，定义数据库连接driver/url/用户名/密码，<strong>在"Manage Drivers..."中把jdbc的jar加进来</strong></p>
<p><br />3、新建Report Parameter，设置参数名称、类型，参数值将来通过url传递或使用程序方式赋值</p>
<p><br />4、新建data sets，设置Query，即preparedStatement语句，设置data set的Pameters，即reparedStatement中的&ldquo;?&rdquo;，使之和report parameter关联；&nbsp;如果data set的Parameter的值不是来自于Report Parameter，例如来自另外一个data set，&nbsp;可不设置关联，而在报表内容中设置和其它data set字段的关联（见步骤8）</p>
<p>5、如果data set对应的sql是动态生成的，可以设置&ldquo;property Binding&rdquo;，在Query Text中输入sql和表达式，其中可以使用Report Parameter和javascript语句，<strong>运行时将执行Property Binding中的Query Text，而不执行Query中的preparedStatement</strong>，但是Query中的preparedStatement在设计报表时还是有用的，因为可以看到运行结果的字段列表，只要preparedStatement和Query Text返回相同的字段即可</p>
<p>6、将定义好的data set拖动到设计界面上，会自动在报表放置一个Table</p>
<p>7、如要对table的某列合计，从Palette视图中拖一个&ldquo;Aggregation&rdquo;到设计界面</p>
<p>8、如要Master-Sleve报表，则在<strong>Table中再放置一个Table</strong>。例如一个data set是客户基本信息，每个客户一条记录，另一个data set是客户交易明细，每个客户可能有多条记录，则先将客户基本信息的data set拖置设计界面，会自动放置一个Table，然后将客户交易明细的data set拖置前一个Table中适当位置，即生成一个Table中的Table。选中交易明细的Table，打开Property Editor视图，在binding栏中，选择交易明细的data set，按&ldquo;Dataset Parameter Binding...&rdquo;，选择&ldquo;CUST_ID"字段，将其和客户基本信息data set的CUST_ID关联起来（row["CUST_ID"]）</p>
<p>9、按需要调整报表的样式，从eclipse工具栏中选择&ldquo;view report in web viewer&rdquo;，如果设置了Report Parameter，会出现一个对话框，要求输入Report Parameter的值，按确定后生成报表内容</p>
<p>10、在web viewer中，鼠标右建点按生成的报表，选&ldquo;属性&rdquo;，获取url，开一个空白IE，复制url到地址栏，在url最后用&ldquo;&amp;参数名=参数值&rdquo;方式，把Report Parameter加进url，按回车，直接出现报表结果，不再出现报表参数输入对话框。还可以pdf/doc/html等格式预览生成的报表</p>
<p><span style="font-size: medium;"><strong>Birt报表部署步骤：</strong></span></p>
<p>部署设计好的报表很简单，只需要下载Birt runtime，将Birt runtime部署为一个web应用，报表设计文件复制到该应用的根目录即可。为方便起见，以下将Birt runtime部属为一个独立的应用，当然也可以将Birt runtime整合到你的web应用中</p>
<p>1、下载birt runtime 2.2.1.1，解开下载文件<br />2、将birt rumtime解开后的目录下的部署为一个web应用，使用JDK1.4.2或以上版本<br />3、将报表设计步骤中生成的设计文件（*.retdesing文件）复制到web应用所在目录<br />4、确保以下文件在WEB-INF/lib目录下存在，如果没有，从all in one中搜索复制过来<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; com.ibm.icu_3.6.1.v20070906.jar<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; coreapi.jar<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; engineapi.jar<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scriptapi.jar<br />5、将jdbc的jar或zip文件复制到WEB-INF\platform\plugins\org.eclipse.birt.report.data.oda.jdbc_2.2.1.r22x_v20070919\drivers目录下<br />6、删除WEB-INF\platform\org.eclipse.birt.report.data.oda.sampledb_2.2.1.r22x_v20070919目录（示例数据库，没什么用处），否则生成报表时有不必要的警告信息出现<br />7、启动web server，修改设计步骤第10步获得的url，使IP/端口/web应用名称符合你的Birt runtime部署，在IE中可以查看报表内容&nbsp;</p>
<p>使用Birt提供API以程序方式生成报表结果，例如生成PDF格式的报表结果，代码片段如下：</p>
<pre name="code" class="java">import java.util.HashMap;
import java.util.logging.Level;

import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayOutputStream;

import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.EngineConstants;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.IReportEngineFactory;
import org.eclipse.birt.report.engine.api.IReportRunnable;
import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
import org.eclipse.birt.report.engine.api.PDFRenderOption;

public class PDFReportServiceAccess
{
    /** *//** 初始化的状态 */
    protected static boolean initStatus = false;

    private static IReportEngine engine = null;

    private static EngineConfig config = null;
    
    private static IReportRunnable design = null;
    
    private static PDFRenderOption ro = null;

    /** *//** 初始化资源 */
    public void initilize()
    {
    if ( initStatus == true )
        return;

    try
    {
        config = new EngineConfig();
        config.setEngineHome( "C:/projects/birt/WEB-INF/platform" );  //birt runtime web应用中的报表引擎目录
        config.setLogConfig( "C:/projects/birt/logs", Level.FINE );

        Platform.startup( config );
        IReportEngineFactory factory = ( IReportEngineFactory ) Platform
            .createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
        engine = factory.createReportEngine( config );
        engine.changeLogLevel( Level.WARNING );

        ro = new PDFRenderOption();
      //  ro.setBaseURL( "http://localhost:8080/birt" ); //birt runtime web应用
        config.getEmitterConfigs().put( "pdf", ro ); //生成pdf格式

        initStatus = true;

    }
    catch ( Exception ex )
    {
        ex.printStackTrace();
        initStatus = false;
    }
    }

    /** *//** 释放资源 */
    public void release()
    {
    engine.shutdown();
    Platform.shutdown();

    initStatus = false;
    }

    protected OutputStream run( String filename, HashMap parameters ) throws EngineException
    {
    design = engine.openReportDesign( filename );
    
    // Create task to run and render the report,
    IRunAndRenderTask task = engine.createRunAndRenderTask( design );
    HashMap contextMap = new HashMap();
    contextMap.put( EngineConstants.APPCONTEXT_PDF_RENDER_CONTEXT, ro );
    task.setAppContext( contextMap );
    task.setParameterValues( parameters );
    task.validateParameters();

    OutputStream os = new ByteArrayOutputStream();
    ro.setOutputStream( os );
    ro.setOutputFormat( "pdf" );
    task.setRenderOption( ro );

    task.run();
    task.close();

    return os;
    }

    /** *//**
         * 生成PDF格式报表,以OutputStream格式返回
         * 
         * @param filename 报表设计文件名全路径
         * @param parameters
         *                报表参数
         * @return ByteArrayOutputStream
         * @throws EngineException
         */
    public OutputStream call( String filename, HashMap parameters ) throws EngineException
    {
    initilize();
    OutputStream os = run( filename, parameters );
    release();
    
    return os;
    }

    /** *//**
         * @param args
         */
    public static void main( String[] args )
    {
    HashMap parameters = new HashMap();
                     //三个Report Parameters，名称必须在报表设计文件中预先定义好
    parameters.put( "begindate", "2004/01/01" );
    parameters.put( "enddate", "2007/12/31" );
    parameters.put(  "sql", " where cust_id = 1234567" );
    
    ByteArrayOutputStream bos = null;

    PDFReportServiceAccess ebr = new PDFReportServiceAccess();

    String filename = "C:/projects/birt/batch_report.rptdesign";
    try
    {
        bos = ( ByteArrayOutputStream ) ebr.call( filename, parameters );
        OutputStream fis = new FileOutputStream( "c:/test.pdf" );
        bos.writeTo( fis );
    }
    catch ( Exception e )
    {
        e.printStackTrace();
    }
    }

}</pre>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://07360591.javaeye.com/blog/200507#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 05 Jun 2008 14:24:49 +0800</pubDate>
        <link>http://07360591.javaeye.com/blog/200507</link>
        <guid>http://07360591.javaeye.com/blog/200507</guid>
      </item>
      <item>
        <title>JasperReport用户手册</title>
        <author>咖啡舞者</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://07360591.javaeye.com">咖啡舞者</a>&nbsp;
          链接：<a href="http://07360591.javaeye.com/blog/196107" style="color:red;">http://07360591.javaeye.com/blog/196107</a>&nbsp;
          发表时间: 2008年05月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>这篇文章转自--疯狂的菠菜</p>
<p>1 简介</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JasperReport是一个强大、灵活的报表生成工具，能够展示丰富的页面内容，并将之转换成PDF，HTML，或者XML格式。最重要的是它是开源的，这给我们带来很大方便，但是文档却要钱，让人不爽。不过人总要生存，再说，做这么一个好东西，用户总不能一点代价也不付（虽然对于中国普通程序原来说太贵了点）。它还有一个相关的开源工程?IReport，这是一个图形化的辅助工具，因为JasperReport仅提供了可使用的类库而未提供更好的开发工具，IReport的出现解决了这一难题。它们配合使用将会更大程度的提高效率。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 该库完全由Java写成，可以用于在各种Java应用程序，包括J2EE，Web应用程序中生成动态内容。它的主要目的是辅助生成面向页面的（page oriented），准备付诸打印的文档。JasperReport借由定义于XML文档中的report design进行数据组织。这些数据可能来自不同的数据源，包括关系型数据库，collections，java对象数组。通过实现简单的接口，用户可以将report library插入到订制好的数据源中，在以后我们将提到有关内容。<br />其实这是一份JasperReport Ultimate Guide的简单翻译以及我的理解和例子。在最后，我将描述一个我正在做的工程，将其中用到的相关信息贡献出来。我这么做是因为当我在学这个类库的时候苦于很少有相关的中文文档，诱惑语焉不详，希望其他人不再受苦。这个文档将分几次贴出来，与原文档的章节相对应。</p>
<p>2 API概览</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上图为一个生成报表并打印（导出）的全过程。我将会把涉及到的重要的类进行一一说明。<br />Class net.sf.jasper.engine.design.JasperDesign<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是一个未经加工的报表实例，供JasperReport Library使用。这个类可以在JasperReport类库内置的XML解析器对XML report design进行解析处理之后得到。如果你的程序不想对直接XML文件进行操作，在例子noxmldesign中有不使用XML设计文件而动态生成这个类的方法。我们稍稍看看这个例子：<br />import 略<br />public class NoXmlDesignApp<br />{<br />private static JasperDesign getJasperDesign() throws JRException<br />{<br />//JasperDesign定义JasperDesign的头信息<br />JasperDesign jasperDesign = new JasperDesign();<br />jasperDesign.setName("NoXmlDesignReport");<br />.剩余略</p>
<p>&nbsp;</p>
<p>//Fonts定义使用到的字体<br />JRDesignStyle normalStyle = new JRDesignStyle();<br />normalStyle.setName("Arial_Normal");</p>
<p>&nbsp;</p>
<p>//Parameters 定义Parameters的内容?这个内容以后会提到<br />JRDesignParameter parameter = new JRDesignParameter();<br />parameter.setName("ReportTitle");<br />parameter.setValueClass(java.lang.String.class);<br />jasperDesign.addParameter(parameter);</p>
<p>&nbsp;</p>
<p>parameter = new JRDesignParameter();<br />parameter.setName("OrderByClause");<br />parameter.setValueClass(java.lang.String.class);<br />jasperDesign.addParameter(parameter);</p>
<p>&nbsp;</p>
<p>//Query 定义查询<br />JRDesignQuery query = new JRDesignQuery();<br />query.setText("SELECT * FROM Address $P!{OrderByClause}");<br />jasperDesign.setQuery(query);</p>
<p>&nbsp;</p>
<p>//Fields<br />JRDesignField field = new JRDesignField();<br />field.setName("Id");<br />field.setValueClass(java.lang.Integer.class);<br />jasperDesign.addField(field);</p>
<p>&nbsp;</p>
<p>//Variables 定义变量<br />JRDesignVariable variable = new JRDesignVariable();<br />variable.setName("CityNumber");<br />variable.setValueClass(java.lang.Integer.class);<br />variable.setResetType(JRVariable.RESET_TYPE_GROUP);</p>
<p>&nbsp;</p>
<p>//Groups 定义组<br />group.setMinHeightToStartNewPage(60);<br />expression = new JRDesignExpression();<br />//余下定义一个文档的其他内容，这里省略</p>
<p>&nbsp;</p>
<p>return jasperDesign;<br />}<br />从getJasperDesign()方法我们可以看出，这个应用程序并没有从XML文件里面将report design提取出来在生成JasperDesign类，而是直接利用JasperDesign提供的函数生成了一个报表设计。这样做的原因是基于灵活性的考虑，你可以在程序中随时动态生成报表，而不需要去从硬盘或网络中读取XML设计文件。但通常我不这么做，因为比较麻烦，而且要对JasperReport的每个元素都非常熟悉才行。<br />Class net.sf.jasper.engine.JasperReport<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个类的实例包含了一个经过编译的report design对象。生成它的时机是对报表编译之后，但尚未对其填入数据的时候。编译过程中，JasperReport需要生成一个临时的类文件，用以保存report expression，如变量表达式，文本，图像表达式，组表达式等等。这个临时的Java Source File是被动态编译的，编译器使用的是JDK中用来执行应用程序的编译器类（compiler class）。如果 tools.jar不在classpath中，编译过程将采用javac.exe来进行后台编译。编译后所得的字节码保存在JasperReport类中，用来在执行期装填数据（filling the report with data）和给表达式赋值（evaluate various report expression）。<br />Class net.sf.jasper.engine.JasperCompileManager<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是一个上面提到的与编译有关的类。利用它提供的一些方法，你将有能力编译从本地硬盘或一个Input Stream获得的XML report；还可以通过传给JasperCompileManager一个JasperDesign类，来对内存中的report design进行编译?功能很强大。<br />Class net.sf.jasper.engine.JasperPrint<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当一个报表已经装填好数据之后，这个文档就以JasperPrint类的实例出现。这个类可以直接用JasperReport内置的viewer进行查看，也可以序列化到硬盘以备后用，或者发送到网上去。这个类的实例是报表装填过程后的产物，它可以被JasperReport类库中的导出方法导出成各种流行的格式?PDF，HTML，XML等等。<br />Interface net.sf.jasper.engine.JRDataSource<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个类与报表的数据源有关。只要能够恰当的实现他的一些接口，用户就可以在报表中使用各种数据源，在报表装填的时候由报表引擎负责对数据进行解释和获取。当报表装填的时候，报表引擎都会在后台生成或提供一个该接口的实例。<br />Class net.sf.jasper.engine.JRResultSetDataSource<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是一个JRDataSource的缺省实现，因为很多报表数据都来源于关系数据库，所以JasperReport缺省包含了这个外覆（wrap）了java.sql.ResultSet对象的实现。<br />这个类可以用来包裹（wrap）用以对报表进行装填的、已经载入的结果集，也可以被报表引擎用来包裹通过JDBC执行完查询后所得的数据----非常有用。<br />Class net.sf.jasper.engine.data.JRTableModelDataSource<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 顾名思义，这个类用于包裹java.swing.table.TableModel类中的数据，它也是实现了JRDataSource接口，用于在Java Swing程序中使用已经被载入到table中的数据来生成报表。<br />Class net.sf.jasper.engine.JREmptyDataSource<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是JRDataSouce接口的最简单实现，这个类用在不需要显示数据源数据而从参数中获取数据的报表和仅需要知道数据源中的实际行数（number of virtual rows）的报表中。<br />JasperReport自带的例子：fonts，images，shapes和unicode中使用这个类对报表进行装填，来模拟没有任何record的数据源，这时所有的field都为null。例如：<br />JasperRunManager.runReportToPdfFile(fileName, null, new JREmptyDataSource());<br />Class net.sf.jasper.engine.JasperFillManager<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个类用来实现报表的数据装填。这个类提供了很多方法来接受各种类型的report design--可以是一个对象，一个文件，或一个输入流。它的输出结果也是多样的：file，Object，output Stream。<br />report的装填引擎需要接收一个可以从中获取数据和value的数据源作为报表参数。参数值（Parameters value）通常使用Java.util.Map来提供，里面包含的KEY是报表的参数名。<br />数据源可以通过两种方式提供，这取决于你的解决方案：<br />通常情况下，用户应该提供一个JRDataSource对象，例如我前面提到的那些。<br />但是大多数的报表都是采用关系数据库中的值来装填数据，所以JasperReport拥有一个内置的缺省行为?让用户在报表设计的时候提供一个SQL查询。在运行期，这个查询将被执行以用来从数据库中获取要装填的数据。在这种情况下，JasperReport仅需要一个java.sql.Connection对象来取代通常的数据对象。JasperReport需要这个连接对象来连接数据库管理系统并执行查询操作。<br />在查询结束之后，JasperReport将自动生成一个JRResultSetDataSource，并将它返回给报表装填过程。<br />Class net.sf.jasper.engine.JRAbstractScriptlet<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个类同样用于报表装填期间，用户可以自己定义一些代码，并由报表引擎在装填过程中执行。这些用户代码可以处理报表数据操作，或在一些定义好的时刻执行，例如page，列，或组的分割处。<br />Class net.sf.jasper.engine.JRDefaultScriptlet<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是一个非常方便的JRAbstractScriptlet的子类。通常情况下你应该选择继承这个类。<br />Class net.sf.jasper.engine.JasperPrintManager<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个类用户提供打印方法，用户可以将整个文档或部分文档传递给它，也可以选择是否显示打印Dialog，这在他的API文档中可以找到，这里不再赘述。<br />Class net.sf.jasper.engine.JasperExportManager<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 顾名思义，这个类负责文档的导出。这个类的具体信息详见API文档。非常明显和清除，没什么好解释的，Just use it即可。<br />Class net.sf.jasper.engine.JasperRunManager<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有时，我们仅仅需要构造一个流行的文档格式，例如PDF，或HTML，而不需要将装填过程后生成的JasperPrint对象保存到硬盘或其他中间媒体上。这时，可以使用这个类来直接将装填过程生成的文档导出到所需的格式。<br />Class net.sf.jasper.view.JRViewer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是一个基于Swing的应用程序，你可以将它视为一个独立组件，用来进行打印预览。用户可以继承这个类，来构造满足自身要求的预览程序。<br />Class net.sf.jasper.view.JasperViewer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个类更像是使用JRViewer的教学组件，它演示了一个Swing应用程序如何装在并显示报表。<br />Class net.sf.jasper.view.JasperDesignViewer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个类用于报表的设计期间，用来预览报表模版。它仅作为一个开发工具存在于类库中。<br />Class net.sf.jasper.engine.util.JRLoader<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 装载器用于报表生成的各个主要阶段?编译，装填等等。用户和引擎都可以利用这个类来装载所需的序列化对象如file，URLs，intput stream等等。这个类最令人感兴趣的函数当属loadOnjectFromLocation(String location)。当用户使用这个类从指定地点装载对象的时候，该函数将首先将location解释为一个合法的URL，如果解析失败，函数将认为所提供的location是硬盘上的一个文件名，并将试图读取它。如果在指定地点没找到文件，它将通过classpath定位一个相应于该location的资源，所有努力失败之后，将抛出异常。</p>
<p>&nbsp;</p>
<p><br />3 主要的任务和过程<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这一节我们将看到对你的XML报表设计进行分析，编译，装填数据，预览结果和导出到其他格式的过程。<br />3.1 XML解析<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JasperReport使用SAX2.0 API对XML文件进行解析。然而，这并不是必须的，用于可以在执行其自行决定使用哪一种XML解析器。<br />JasperReport使用org.xml.sax.helpers.XMLReaderFactory类的createXMLReader()来获得解析器实例。在这种情况下，就像在SAX2.0文档中说的那样，在运行期，把Java系统属性org.xml.sax.driver(这是属性的key)的值(value)设定为SAX driver类的全限定名是必要的。用户可以通过两种方法做到这一点，我稍后将解释全部两种方法。如果你想使用不同的SAX2.0XML解析器，你需要指定相应的解析器类的名字。<br />设置系统属性的第一种方法是在你启动Java虚拟机的时候，在命令行使用-D开关：java ?Dorg.xml.sax.driver=org.apache.serces.parsers.SAXParser mySAXApp sample.xml<br />在JasperReport提供的所有例子中，都采用ANT构建工具来执行不同的任务。我们通过使用内置的 task中的元素来提供这一系统属性：</p>
<p>第二种设置系统属性的方法是使用java.lang.System.setProperty(String key, String value)<br />System.setProperty(&ldquo;org.xml.sax.driver&rdquo;,&rdquo; org.apache.xerces.parsers.SAXParser&rdquo;);<br />Jsp/compile.jsp和web-inf/class/servlets/CompileServlet.java文件提供了这方面的例子。<br />注：对于第二种方法，我要说些题外话。有关于JVM的系统属性（我们可以通过System.out.println（System.getProperty(&ldquo;PropertyKey&rdquo;)来查看），可以在运行期像上面说所得那样用System.setProperty(&ldquo;propertyKey&rdquo;,&rdquo;propertyValue&rdquo;);来进行设置。但是一旦JVM已经启动之后，其内建的系统属性，如user.dir，就不能再被更改。奇怪的是我们仍可以用System.setProperty()方法对其进行设置，而在用System.out.println(System.getProperty())方法进行查看的时候发现，其值已经更改为我们设置的值，但事实上我们设置的值不会起任何作用。所以对于内置的属性，我们只能通过-D开关在JVM执行之前进行设置。对于org.xml.sax.driver，由于它不是系统内建属性，所以仍然可以在JVM启动之后加以设置。更详细的信息可以参考王森的〈Java深度历险〉。</p>
<p>&nbsp;</p>
<p><br />3.2 编译报表设计(Report Designs)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了深成一个报表，用户需要首先生成报表的设计（report&rsquo;s design），生成方法或采用直接编辑XML文件，或通过程序生成一个net.sf.jasper.engine.design.JasperDesign对象。本文中，我将主要采用编辑XML文件的方法，因为这种方法在目前是使用JasperReport类库的最好的方法，并且我们有机会更好的了解类库的行为。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 先前提到过，XML报表设计是JasperReport用来生成报表的初级材料（raw meterial）。这是因为XML中的内容需要被编译并载入到JasperDesign对象中，这些对象将在报表引擎向其中填入数据之前经过编译过程。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意：大多数时候，报表的编译被划归为开发时期的工作。你需要编译你的应用程序报表设计，就像你编译你的Java源文件一样。在部署的时候，你必须将编译好的报表，连同应用程序一起安装到要部署的平台上去。这是因为在大多数情况下报表设计都是静态的，很少用应用程序需要提供给用户在执行期编译的，需要动态生成的报表。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 报表编译过程的主要目的是生成并装载含有所有报表表达式（report expression）的类的字节码。这个动态生成的类将会被用来在装填数据，并给所有报表表达式求值（evaluate）的时候使用。具体例子是，如果你用IReport生成一个报表名字叫SimpleSheetTest，它的XML设计文件名叫SimpleSheetTest.jrxml，同时和它在同一目录下IReport会自动生成一个文件名为SimpleSheetTest.java，里面主要是一些报表元素，如Field，Parameters，Variables的定义，以及一些求值表达式。当然，像上面提到的，这个文件在你直接使用JasperReport API的时候是看不到的，因为它是在执行期生成的一个Class。要想看到它的办法是：在IDE（JBuilder，Eclipse）中单步执行程序，在报表打印的阶段，你将能跟踪到这个类，它的名字就是&ldquo;你的报表名.java&rdquo;，按上面的例子就是SimpleSheetTest.java，这和IReport是一致的。当然也可以像下面说的那样，到生成这个类的临时目录里找到它。<br />在这个类生成过程之前，JasperReport引擎需要验证报表设计的一致性（consistency），哪怕存在一处验证检查失败都不会继续运行下面的工作。在下面的章节，我将会展示报表设计验证成功之后的状况。<br />对于这个包含了所有报表表达式（report expressions）的类的字节码，我们至少需要关心三个方面的内容：<br />&nbsp;临时工作目录（temporary workingl directory）<br />&nbsp;Java 编译器的使用l<br />&nbsp;Classpathl</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了能够编译Java源文件，这个文件必须被创建并且被保存到磁盘上。Java编译过程的输出是一个.class文件，这个包含所有报表表达式的类在这个工作目录里被创建并编译，这也是为什么JasperReport需要访问这个临时目录的原因。当报表的编译过程结束之后，这些临时的类文件将被自动删除，而生成的字节码将保存在net.sf.jasper.engine.JasperReport对象中。如果需要的话，这个类可以将自己序列化（serialized itself）并保存到磁盘上。这就是IReport的做法。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 缺省情况下，这个临时工作目录就是启动JVM时的当前目录，这却取决于JVM的系统属性user.dir。通过更改系统属性jasper.report.compile.temp，用户可以很容易更改这个工作目录。在Web环境下，特别是当你不想让含有启动Web Server的批处理文件的目录和报表编译过程的临时工作目录混在一起的时候，修改这个属性就可以了。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面提到的第二个方面涉及用来编译报表表达式类的Java编译器。首先，报表引擎将试图使用sun.tools.javac.Main类来编译Java源文件。这个类包含在tools.jar中，当且仅当这个jar文件在JDK安装目录下的bin/目录中，或在classpath中时，sun.tools.javac.Main才能正常使用。<br />如果JasperReport不能成功装载sun.tools.javac.Main文件，程序将动态执行java编译过程，就像我们通常用命令行那样，使用JDK安装目录下的bin/目录下的javac.exe。这就是为什么将JDK安装目录/lib/下的tools.jar文件copy到JasperReport工程的lib/目录下是一个可选的操作（optional operation）。如果tools.jar不在classpath中，JasperReport将显示错误信息并继续上面提到的操作。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当编译Java源文件的时候，最重要的事情莫过于classpath。如果Java编译器不能在指定的classpath中找到它试图编译的所有相关类的源文件，则整个过程将失败并停止，错误信息将在控制台显示出来。同样的事情也将发生在JasperReport试图编译报表表达式类的时候。所以，在runtime为编译过程提供正确的classpath是非常重要的。例如，我们我们需要确认在classpath中，我们提供了在报表表达式中可能用到的类（custom class）。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在这个方面也有一个缺省的行为。如果没有为编译report class特殊指定classpath，引擎将会使用系统属性java.class.path的值来确定当前的JVM classpath。如果你指定了系统属性jasper.reports.compile.class.path的值，你可以用你定义的classpath来覆盖缺省行为。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 大多数情况下，编译一个report只需要简单的调用JasperReport类库中的JasperCompileManager.compileReport(myXmlFileName);即可。调用之后将生成编译好的report design并存储在.jasper文件中，这个文件将会保存在和提供XML report design文件相同的目录中。</p>
<p>&nbsp;</p>
<p><br />3.3 Report Design 预览<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JasperReport类库并没有提供高级的GUI工具来辅助进行设计工作。但是目前已经有至少4个project试图提供这样的工具。然而，JasperReport本身提供了一个很有用的可视化组件来帮助报表设计者在编译的时候预览报表设计（其实不如直接用IReport方便）。<br />net.sf.jasper.view.JasperDesigner是一个基于Swing的Java应用程序，它可以载入并显示XML形式或编译后的报表设计。尽管它不是一个复杂的GUI应用程序，缺乏像拖拽可视化报表元素这样的高级功能，但是它仍然是一个有用的工具（instrument）。所有JasperReport工程提供的例子都利用了这个报表查看器（report viewer）。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你已经安装了ANT（别告诉我你不知道什么是ANT），想要查看一个简单的报表设计（JasperReport工程所带例子），你只需要到相应的文件夹下输入如下命令：<br />〉ant viewDesignXML 或者 〉ant viewDesign<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你没安装ANT，要达到上面的效果就不是很容易，因为JasperReport本身需要一些其他辅助的jar包（在JasperReport安装目录/lib下），在运行的时候，你需要把这些jar包都包含到你的classpath里面，并且正确设计系统属性，如上面提到的org.xml.sax.driver。我可以展示一下在windows下的例子：<br />&gt;java -classpath ./;../../../lib/commons-digester.jar;<br />../../../lib/commons-beanutils.jar;../../../lib/commons-collections.jar;<br />../../../lib/xerces.jar;../../../lib/jasperreports.jar<br />-Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser<br />dori.jasper.view.JasperDesignViewer -XML -FFirstJasper.xml<br />很麻烦吧？还是赶快弄个ANT吧。下面是预览之后的结果（其实用IReport更好）</p>
<p>&nbsp;</p>
<p>3.4报表装填（Filling Report）<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 报表装填（report filling）过程是JasperReport library最重要的功能。它体现了这个软件最主要的目的（main objective），因为这一过程可以自由的操作数据集（data set），以便可以产生高质量的文档。有3种材料需要装填过程中作为输入提供给JasperReport：<br />&nbsp;report design（reportl templet）<br />&nbsp;参数（parameters）l<br />&nbsp;数据源（datal source）<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这一过程的输出通常是一个单一的最终要被查看，打印或导出到其他格式的文档。<br />要进行这一过程，我们需要采用net.sf.jasper.engine.JasperFillManager类。这个类提供了一些方法来让我们装填报表设计（report design），report design的来源可以是本地磁盘，输入流，或者直接就是一个已存在于内存中的net.sf.jasper.engine.JasperReport类。输出的产生是于输入类型相对应的，也就是说，如果JasperFillManager接到一个report design的文件名，装填结束后生成的report将会是一个放在磁盘上的文件；如果JasperFillManager收到的是一个输入流，则生成的report将会被写道一个输出流中。<br />有些时候，这些JasperFillManager提供的方法不能满足某些特定的应用的要求，例如可能有人希望他的report design被作为从classpath中得到的资源，并且输出的报表作为一个文件存放在一个指定的磁盘目录下。遇到这种情况时，开发人员需要考虑在将报表设计传递给报表装填过程之前，用net.sf.jasper.engine.util.JRLoader类来装载report design对象。这样，他们就能获得像报表名这样的report design属性，于是开发者就能生成最终文档的名字（construct the name of the resulting document），并将它存放到所需的位置上。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在现实中，有许多报表装填的情境（scenarios），而装填管理器仅试图覆盖其中最常被使用到的部分。然而对于想要自己定制装填过程的人来说，只要采用上面所说的方法，任何开发者都可以达到满意的结果。<br />报表参数通常作为java.util.Map的value提供给装填管理器，参数名为其键值（key）。<br />作为装填过程所需的第三种资源?数据源，有如下两种情况：<br />通常，引擎需要处理net.sf.jasper.engine.JRDataSource接口的一个实例，通过这个实例，引擎可以在装填过程中获取所需数据。JasperFillManager提供的方法支持所有的JRDataSource对象（这是一个Interface，上面一章提到过它的常用实现）。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然而，这个管理器还提供一些接受java.sql.Connection对象作为参数的方法集，来取代所需的数据源对象。这是因为在很多情况下，报表生成所需的数据都来源于某个关系型数据库中的表（table）。<br />在报表中，用户可以提供SQL查询语句来从数据库中取回报表数据（report data）。在执行期，engine唯一需要做的是获得JDBC connection对象，并使用它来连接想要连接的数据库，执行SQL查询并取回报表数据。在后台，引擎将使用一个特殊的JRDataSource对象，但是它对于调用它的程序来说是透明的。<br />JasperReport工程提供了相关的例子，它们采用HSQL数据库服务器（在工程文件中，有一个相应的文件夹），要运行这些例子你需要首先启动该服务器，方法是：在/demo/hsqldb目录下输入如下命令：&gt;ant 或者 &gt;ant runServer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没装ANT就麻烦点：&gt;java -classpath ./;../../lib/hsqldb.jar org.hsqldb.Server<br />一下代码片断显示了query例子是如何装填数据的：<br />//Preparing parameters<br />Map parameters = new HashMap();<br />parameters.put("ReportTitle", "Address Report");<br />parameters.put("FilterClause", "'Boston', 'Chicago', 'Oslo'");<br />parameters.put("OrderClause", "City");<br />//Invoking the filling process<br />JasperFillManager.fillReportToFile(fileName, parameters, getConnection());</p>
<p>&nbsp;</p>
<p><br />3.5 查看报表（Viewing Reports）<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 报表填充阶段的输出通常是一个JasperPrint对象，如果把它保存在磁盘上，通常以一个.jrprint文件的形式存在。JasperReport拥有一个内置的查看器，用来查看用内置的XML导出器（XML exporter）获得的XML格式的报表文件。这个查看器就是以前提到过的net.sf.jasper.niew.JRViewer?一个基于Swing的应用程序组件，用户可以通过继承这个类来定制自己所需的查看器。JasperReport工程中自带的例子webapp中，你可以阅读JRViewerPlus类的代码来获取进一步内容。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意：JasperViewer更像是一个教人们如何使用JRViewer组