﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-数码民工</title><link>http://www.cnblogs.com/maxpain/</link><description>生活只有在平淡无味的人看来才是空虚而平淡无味的。 —— 车尔尼雪夫斯基</description><language>zh-cn</language><lastBuildDate>Mon, 06 Jul 2009 06:34:15 GMT</lastBuildDate><pubDate>Mon, 06 Jul 2009 06:34:15 GMT</pubDate><ttl>60</ttl><item><title>Jabber 技  术  概  况 [收藏]</title><link>http://www.cnblogs.com/maxpain/archive/2006/06/09/421424.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Fri, 09 Jun 2006 03:05:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2006/06/09/421424.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/421424.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2006/06/09/421424.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/421424.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/421424.html</trackback:ping><description><![CDATA[<div class=BlogPostContent>
<h1 align=center><span>Jabber </span><span>技</span><span><span>&nbsp; </span></span><span>术</span><span><span>&nbsp; </span></span><span>概</span><span><span>&nbsp; </span></span><span>况</span><span><o:p></o:p></span></h1>
<p class=MsoNormal align=center><b><span>Jabber</span></b><b><span>即时通信系统服务整体框架的概述</span></b><b><span><o:p></o:p></span></b></p>
<h2><span><font face=Arial>1</font></span><span>、</span><span><font face=Arial>Jabber</font></span><span>技术概述</span></h2>
<p class=MsoNormal align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>本文档包括以下内容：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Introduction </span><span>简介</span></p>
<p class=MsoNormal align=left><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Foundations </span><span>基本知识</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>High-Level Server Architecture</span><span>高阶服务体系</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Basic Message Flow</span><span>基本信息流程</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Authentication </span><span>鉴定</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Jabber Session Manager Jabber</span><span>会话管理</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Threading </span><span>线程</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Deliver Logic</span><span>发送逻辑</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Transport </span><span>传送器</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Subscriptions </span><span>订阅</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Jabber IDs</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Server Dialback </span><span>服务</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Conclusion </span><span>结束语</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Copyright Information </span><span>版权信息</span></p>
<h3><span><span><font size=5>1.1.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span>Introduction </span><span>简介</span></font></h3>
<p class=MsoNormal><span>第一个</span><span>Jabber</span><span>技术的应用是由开源社区发起并一直领导的即时消息的实时系统。</span><span>Jabber</span><span>即时消息（</span><span>IM</span><span>）系统和现有</span><span>IM</span><span>服务相比较由以下几个关键特点：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>XML</span><span>为基础</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>分布式网络</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>开放的协议和内核代码</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>模块化的、可扩展的系统架构</span></p>
<p class=MsoNormal align=left><span>本文档提供一个关于</span><span>Jabber</span><span>系统架构的高阶概述，主要集中介绍</span><span>Jabber</span><span>开源服务器的设计，目前的版本是</span><span>1.4</span><span>（译注：目前最新版本是</span><span>2.0</span><span>）。关于</span><span>Jabber</span><span>的</span><span>XML</span><span>协议的相关内容，请参见</span><span>Jabber Protocol Overview</span><span>参考文档（</span><span><a href="http://docs.jabber.org/general/html/protocol.html">http://docs.jabber.org/general/html/protocol.html</a></span><span>）。</span></p>
<p class=MsoNormal align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（注：本文档综合</span><span>了以下文件内容：</span><span>Jeremie Miller 1999<span><span>年</span></span>11<span><span>月</span></span>19<span><span>日</span></span><span>的《</span>Jabber</span><span>系统架构概况》，</span><span>Peter Millard </span><span>在</span><span>2000</span><span>年</span><span>4</span><span>月</span><span>25</span><span>日</span><span>关于此文档的上一个版本，</span><span>Peter Saint-Andre 2000<span><span>年</span></span>11<span><span>月</span></span>6<span><span>日</span></span><span>的《</span>Jabber</span><span>白皮书》</span><span><o:p></o:p></span></p>
<h3><span><span><font size=5>1.2.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span>Foundations </span><span>基础知识</span><span><o:p></o:p></span></font></h3>
<p class=MsoNormal><span>Jabber</span><span>在设计上很大程度上沿袭了</span><span>Internet</span><span>上最成功的消息系统：即</span><span>email</span><span>。这样</span><span>Jabber</span><span>就可以在一个使用共同协议的服务器组成的分布式网络上提供通信，连接这个网络的客户端，可以象接收消息一样发送消息给同一个服务器或其他</span><span>Internet</span><span>上的服务器上的用户。不过，尽管</span><span>email</span><span>是一个存储－转发系统，但</span><span>Jabber</span><span>转发消息却是实时的，因为</span><span>Jabber</span><span>服务器（连同其他所有</span><span>Jabber</span><span>服务器在内）知道一个用户什么时候在线。这个能力被成为在线，也是即时消息的核心所在。</span><span>Jabber</span><span>通过两个附加功能提供这些</span><span>IM</span><span>标准特性，这也使得</span><span>Jabber</span><span>与众不同。首先是一个允许消息系统间协同作业的开放协议。其次是建立在</span><span>XML</span><span>上的强大根本，它使得非但是两个人之间的通信，甚至是应用软件之间的通信成为了可能。</span></p>
<p class=MsoNormal><span>上述每一个功能都将在下文进行进一步的阐述，并进一步扩展本文档的内容。</span></p>
<h4><font size=5><font face=Arial><span><span>1.2.1.</span></span><span> Client/Server </span></font><span>客户端</span><span><font face=Arial>/</font></span><span>服务端</span></font></h4>
<p class=MsoNormal><span>Jabber</span><span>使用的是客户端－服务端的系统架构，而不是其它一些即时消息系统使用的客户端－客户端的系统架构。所有从一个客户端发给另一个客户端的</span><span>Jabber</span><span>消息和数据都必须通过服务端。任何一个客户端都可以通过商议与另一个客户端自由地建立一个直接地连接，但这些连接只用于特殊服务地应用。有一些实例被鼓励建立这种连接，比如文件传输，但这些实例必须先通过一个客户端－服务端形势进行协商，才能建立。</span></p>
<h4><font size=5><font face=Arial><span><span>1.2.2.</span></span><span> Distributed Network </span></font><span>分布式网络</span></font></h4>
<p class=MsoNormal><span>Jabber</span><span>地网络体系是模仿</span><span>e-mail</span><span>系统地。每一个用户都有自己的本地服务器，并从该服务器上接收信息，消息和在线信息在这些服务器之间传输。可以添加任意数目的</span><span>Jabber</span><span>服务器，这些服务器接受客户端的连接，并与其它</span><span>Jabber</span><span>服务器进行通信。每一个</span><span>Jabber</span><span>服务器都独立于其他</span><span>Jabber</span><span>服务器，并且拥有其自身的用户列表。通过</span><span>Internet</span><span>，任一</span><span>Jabber</span><span>服务器都可以与其他</span><span>Jabber</span><span>服务器进行通话。每一个用户都与一个特殊服务器（提供注册服务的服务提供商或行政管理企业）相对应，</span><span>Jabber</span><span>地址和</span><span>email</span><span>地址的形势是一样的，如：</span><span><a href="mailto:stpeter@jabber.org">stpeter@jabber.org</a></span><span>（下面的</span><span>Jabber ID</span><span>部分将介绍更多关于</span><span>Jabber</span><span>地址的信息）。</span></p>
<h4><font size=5><font face=Arial><span><span>1.2.3.</span></span><span> Modular Server </span></font><span>模块化的服务器端</span></font></h4>
<p class=MsoNormal><span>Jabber</span><span>服务器遵循两个主要法则：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>监听客户端连接，并直接与客户端应用程序通信</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>与其他</span><span>Jabber</span><span>服务器通信</span></p>
<p class=MsoNormal><span>Jabber</span><span>开源服务器被设计成模块化，由各个不同的代码包构成，这些代码包分别处理类似用户认证、数据存储（离线消息，花名册，用户信息等）等等。另外，服务器可以通过附加服务来进行扩展，如完整的安全策略，允许服务器组件的连接或客户端选择，通向其他消息系统的网关。</span></p>
<p class=MsoNormal><span>一个模块化的例子就是通过</span><span>Jabber XML</span><span>翻译成其他协议的独立&#8220;</span><span>transport</span><span>&#8221;（传输器），可以实现</span><span>Jabber</span><span>消息系统与非</span><span>Jabber</span><span>消息系统之间进行消息和在线信息的交流。这些传输器并不是服务器内核。相反，它们是很容易添加到服务器内核服务器端程序，为终端用户提供更强大的功能服务。</span></p>
<h4><font size=5><font face=Arial><span><span>1.2.4.</span></span><span> Simple Client </span></font><span>简单的客户端</span></font></h4>
<p class=MsoNormal><span>Jabber</span><span>系统的一个设计标准是必须支持简单的客户端（如同和</span><span>telnet</span><span>连接一样简单的客户端）。事实上，</span><span>Jabber</span><span>系统架构对客户端只有很少的几个限制。一个</span><span>Jabber</span><span>客户端必须支持的功能有：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>通过</span><span>TCP </span><span>套接字与</span><span>Jabber</span><span>服务器进行通信</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>解析组织好的</span><span>XML</span><span>信息包</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>理解消息数据类型</span></p>
<p class=MsoNormal><span>Jabber</span><span>将复杂性从客户端转移到服务器端。这使得客户端编写变得非常容易（一个证据就是今天出现了种类繁多的客户端），更新系统功能也同样变得容易（这样，就不用强迫用户去下载新的客户端）。</span><span>Jabber</span><span>客户端与服务端通过</span><span>XML</span><span>在</span><span>TCP </span><span>套接字的</span><span>5222</span><span>以上端口进行通信，而不需要客户端之间直接进行通信。在实际应用中，许多低阶的客户端功能（如解析</span><span>XML</span><span>，理解基本的</span><span>jabber XML</span><span>语言类似</span><span>&lt;message/&gt;</span><span>，</span><span>&lt;presence/&gt;</span><span>，</span><span>&lt;iq/&gt;</span><span>）已经包含在</span><span>Jabber</span><span>客户端类库中，这样可以让客户端的开发人员更多的注重用户界面的开发。</span></p>
<h4><font size=5><font face=Arial><span><span>1.2.5.</span></span><span> XML Data Format XML</span></font><span>数据格式</span></font></h4>
<p class=MsoNormal><span>XML</span><span>是</span><span>Jabber</span><span>系统架构的核心部分，它最重要的作用是系统的底层可扩展性，并能表述几乎任何一种结构化数据。（特别地，</span><span>Jabber</span><span>利用</span><span>XML</span><span>数据流进行客户端－服务器端以及服务器端－服务器端的通信。</span><span>XML</span><span>数据流一般是由客户端发起至服务端，</span><span>XML</span><span>数据流的有效时间直接与用户的在线会话有效时间相关联。）</span></p>
<p class=MsoNormal align=left><span>Jabber</span><span>严格遵守</span><span>XML</span><span>的同时，不需要知道任何关于信息转发中介的信息：对于信息转发中介没有任何固有的规定，也不需要任何关于信息转发中介的系统架构的知识。这都是可能的，在另一方面，这也使得提供与第三方服务（如：</span><span>IRC</span><span>，</span><span>ICQ</span><span>，</span><span>AIM</span><span>）进行信息传输的传输器的实现成为可能。而在</span><span>Jabber</span><span>系统内部，就像</span><span>Jabber</span><span>系统中其它每一个组件一样，传输器使用</span><span>XML</span><span>语音。更多关于</span><span>Jabber XML</span><span>协议的信息可以在《</span><span>Jabber</span><span>协议概述》（</span><span><a href="http://docs.jabber.org/general/html/protocol.html">http://docs.jabber.org/general/html/protocol.html</a></span><span>）中。</span></p>
<h3><span><span><font size=5>1.3.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span><span>&nbsp;</span>High-Level Server Architecture </span><span>高阶服务器系统架构</span></font></h3>
<p class=MsoNormal><span>Jabber</span><span>服务器由若干个组件构成，这些组件分别完成</span><span>Jabber</span><span>系统中逻辑上独立的各个功能。服务器的内核是一个转发组件，这个组件的唯一功能就是从一个基本组件往另一个基本组件进行</span><span>XML</span><span>解析传递。共有四个这样的基本组件：接收、连接、执行、装入。这些基本组件解析传入的</span><span>XML</span><span>，转发给其他基本组件，并使得基本组件的下游组件能够连续的使用</span><span>XML</span><span>。下面是一个高阶的系统架构的演示图：</span><span> </span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span><o:p><a href="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_1.JPG"><img height=480 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_1.jpg" width=381 border=0></a></o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span>一个服务器启动后，</span><span>Jabber</span><span>服务器负责注册的组件通过</span><span>Jabber</span><span>的主程序后台（如同在服务器的配置文件中定义的一样）执行其功能单元（？），并运行由这些功能单元组成的信息包（以此来定义所有信息包的传送逻辑）。</span><span>Jabber</span><span>服务器的内核包括处理以下公共任务的组件：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>会话管理</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>客户端－服务端的通信</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>服务器－服务器的通信</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>DNS</span><span>解决方案</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>用户认证</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>用户注册</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>数据库查询</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>为离线用户存储信息</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>存储并找回</span><span>vCards</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>根据用户设定过滤信息</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>群组聊天（多对多的通信）</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>系统日志</span></p>
<p class=MsoNormal><span>另外，服务器内核能够补充&#8220;传输器&#8221;，这些&#8220;传输器&#8221;被设计来解决不同于</span><span>Jabber</span><span>开放的</span><span>XML</span><span>格式的其他协议。（详情见传输器部分）。这些传输器可以很自然地作为整体服务器系统架构的内置组件存在。目前存在进行翻译功能的传输器主要是针对以下的协议：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>AOL Instant Messenger</span><span>（</span><span>AIM</span><span>）</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>ICQ</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Internet Relay Chat</span><span>（</span><span>IRC</span><span>）</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>MSN Messenger</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Rich Site Summary</span><span>（</span><span>RSS 0.9</span><span>）</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Yahoo! Messenger</span></p>
<p class=MsoNormal><span>（注：附加的传输器可以根据需要增加到</span><span>Jabber</span><span>上，例如为了解决</span><span>IM</span><span>不统一的格式，但未来的传输器没有在本文档中阐述。）</span></p>
<h3><span><span><font size=5>1.4.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span><span>&nbsp;</span>Basic Message Flow </span><span>基本消息流程</span></font></h3>
<p class=MsoNormal><span>对于学习</span><span>Jabber</span><span>系统而言，研究通过服务器的典型数据流程是一个好的入门方式。（当</span><span>XML</span><span>的&#8220;消息&#8221;元素仅指</span><span>Jabber</span><span>开放的</span><span>XML</span><span>协议中规定的三种主要元素中的一种时，它更能体现</span><span>Jabber</span><span>最核心的意图：通过使用</span><span>XML</span><span>进行消息的点对点发送。）</span></p>
<p class=MsoNormal><span>下面是关于该数据流程的图表：</span></p>
<p class=MsoNormal><span></span></p>
<p class=MsoNormal><span><o:p><br><img height=480 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_2.JPG" width=423 border=0><br>&nbsp;</o:p></span></p>
<p class=MsoNormal><span>Jabber</span><span>服务器（在上述图表中简化为&#8220;</span><span>jabberd</span><span>&#8221;，原义为&#8220;</span><span>Jabber daemon [Jabber</span><span>后台程序</span><span>]</span><span>&#8221;）在主机上的用户会话的上下文中接收型为&#8220;消息&#8221;的包体，正常情况下，该包体在</span><span>5222</span><span>端口（如果</span><span>SSL</span><span>允许并运行的情况下也可以是</span><span>5223</span><span>端口）通过一个直接的</span><span>TCP</span><span>套接字产生。如果会话不存在，</span><span>jabberd</span><span>将发起认证流程，该流程将会在下面的认真部分中进行介绍。如果会话存在，消息包将被送往</span><span>Jabber</span><span>会话管理组件（简称&#8220;</span><span>JSM</span><span>&#8221;）。</span></p>
<p class=MsoNormal><span>下面是一个</span><span>XML</span><span>的例子：</span></p>
<p class=MsoNormal><span>&lt;message<o:p></o:p></span></p>
<p class=MsoNormal><span>to=&#8217;psaintandre@aim.jabber.org&#8217;<o:p></o:p></span></p>
<p class=MsoNormal><span>type=&#8217;chat&#8217;&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span><span>&nbsp;</span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;body&gt;Hey, the AIM transport is working great!&lt;/body&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/message&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>接着，</span><span>JSM</span><span>根据</span><span>Jabber</span><span>服务器的内部配置文件上的服务器名单查找目标服务器的主机名。通常主机名都会被定义；比如，</span><span>aim.jabber.org</span><span>在</span><span>Jabber.com</span><span>服务器上的配置文件被定义为指向该主机的</span><span>AIM</span><span>传输器（该传输器可能在一台单独的机器上）。如果主机名没有在配置文件中被定义，&#8220;</span><span>dnsrv</span><span>&#8221;组件将把这个主机名于一个</span><span>IP</span><span>地址和端口进行对应。另外，由于该主机有问题，消息包将会送到服务器到服务器（</span><span>s2s</span><span>）组件，在这个例子中，</span><span>jabber.org</span><span>。服务器到服务器组件将直接从指定的外部</span><span>Jabber</span><span>服务器（比如</span><span>jabber.org</span><span>）或该主机上一个传输器传入。在上面的例子中，消息包有意传递到</span><span>aim.jabber.org</span><span>上的一个地址，因此，这个包将被送到</span><span>jabber.org</span><span>上的</span><span>AIM</span><span>传输器，再传送到一个</span><span>AOL Instant Messenger </span><span>帐号（见下面的传输器部分）。另一个方面，最终的结果是一个消息从一个</span><span>Jabber</span><span>客户端流通过一个</span><span>Jabber</span><span>服务器流动到另一个</span><span>Jabber</span><span>服务器或外部</span><span>IM</span><span>系统。</span></p>
<h3><span><span><font size=5>1.5.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span><span>&nbsp;</span>Authentication </span><span>认证</span></font></h3>
<p class=MsoNormal><span>在基本消息流程中提到，消息和在线信息是通过</span><span>Jabber</span><span>服务器上一个运行中的主机上的一个用户会话的上下文发送给</span><span>Jabber</span><span>的。在</span><span>Jabber</span><span>协议中规定，这个会话由两个</span><span>XML</span><span>流保持，一个是从客户端到服务器端，另一个是从服务器端到客户端。下面是一个会话的</span><span>XML</span><span>显示：</span></p>
<p class=MsoNormal><span>SEND:&lt;stream:stream<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:to=&#8217;jabber.org&#8217;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:xmlns=&#8217;jabber:client&#8217;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:xmlns:stream=&#8217;http://etherx.jabber.org/streams&#8217;&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span>RECV:&lt;stream:stream<o:p></o:p></span></p>
<p class=MsoNormal><span>RECV:xmls:stream=&#8217;http://etherx.jabber.org/streams&#8217;<o:p></o:p></span></p>
<p class=MsoNormal><span>RECV:id=&#8217;39ABA7D2&#8217;<o:p></o:p></span></p>
<p class=MsoNormal><span>RECV:xmlns=&#8217;jabber:client&#8217;<o:p></o:p></span></p>
<p class=MsoNormal><span>RECV:from=&#8217;jabber.org&#8217;&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;iq id=&#8217;1&#8217; type=&#8217;set&#8217;&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;query xmlns=&#8217;jabber:iq:auth&#8217;&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;username&gt;stpeter&lt;/username&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;resource&gt;Gabber&lt;/resource&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;digest&gt;file881517e9917bb815fed112d811d32b4e4b3aed&lt;/digest&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;/query&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;/iq&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>RECV:&lt;iq id=&#8217;6&#8217; type=&#8217;result&#8217;/&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>(XML for user session goes here)<o:p></o:p></span></p>
<p class=MsoNormal><span>SEND:&lt;/stream:stream&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span>RECV:&lt;/stream:stream&gt;<o:p></o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span>为了让服务器建立一个会话，首先必须对用户进行认证。下面的图表展示的就是认证的活动流程：<br><br></span><span><o:p></o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;<img height=480 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_3.JPG" width=488 border=0></o:p></span></p>
<p class=MsoNormal><span></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span>当客户端连接到主机，并发起一个</span><span>XML</span><span>流时，认证流程就开始了。</span><span>Jabber</span><span>服务器会立即在</span><span>&#8217;jabber:iq:auth&#8217;</span><span>的名字空间中对</span><span>&#8217;iq&#8217;</span><span>（</span><span>info/query</span><span>的简称）类型和</span><span>&#8217;query&#8217;</span><span>子类型的包体进行查询，该名字空间含有对用户的认证信息。认证信息必须包含一个用户名和明文密码（很明显，这是让人沮丧的），一个使用</span><span>SHA1</span><span>算法（这个默认的认证是设计为</span><span>a.k.a</span><span>的&#8220;数字认证&#8221;）加密的密码，或者是一些符合零度认证的数据。</span></p>
<p class=MsoNormal><span>一旦认证信息被接收到，</span><span>XML</span><span>解释器发送控制命令给</span><span>Jabber</span><span>服务器的&#8220;传送&#8221;组件，该组件将把从客户端未等待认证结果就发送过来的</span><span>XML</span><span>进行缓存。主机（通常，但不全是以</span><span>JSM</span><span>形式存在）将把认证包传送到</span><span>Jabber</span><span>服务器的</span><span>&#8217;xdb&#8217;</span><span>组件。</span><span>xdb</span><span>组件（</span><span>&#8217;xdb&#8217;</span><span>即&#8220;</span><span>Xml Data Base</span><span>&#8221;――</span><span>XML</span><span>基数据）将把认证包发送给任一注册了该认证包类型的子组件：例如，明文认证包可能通过检查文件系统中的</span><span>XML</span><span>文件用于</span><span>&#8217;xbd_file&#8217;</span><span>子组件，而数字认证包通过检查</span><span>LDAP</span><span>用于</span><span>&#8217;xdb_ldap&#8217;</span><span>子组件。传送组件不作任何处理将认证包传送给</span><span>xdb</span><span>组件，</span><span>xdb</span><span>组件将把该认证包发送给合适的子组件。另外，为了提高性能，</span><span>xdb_ldap</span><span>组件拥有其独立的线程池，其运作方式与会话管理器中的线程模式类似。</span></p>
<p class=MsoNormal><span>Xdb</span><span>组件将认证查询的结果返回给主机（同样，通常是</span><span>JSM</span><span>）。如果认证失败，服务器将返回错误代码</span><span>401</span><span>给客户端而不发起一个会话。如果认证成功，</span><span>JSM</span><span>将开启一个会话（如果需要的话将释放</span><span>XML</span><span>缓存），所有在线信息，消息，以及</span><span>iq</span><span>基本信息在用户会话的上下文中进行来回传递，直到客户端或服务端通过发送一个关闭数据流的标志（</span><span>&lt;/stream&gt;</span><span>）终止。</span></p>
<h3><span><span><font size=5>1.6.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span>Jabber Session Manager<span>&nbsp; </span>Jabber</span><span>会话管理器</span></font></h3>
<p class=MsoNormal><span>下面是</span><span>Jabber</span><span>会话管理器的活动流程：</span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span></span></p>
<p class=MsoNormal><span><img height=480 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_4.JPG" width=491 border=0><br><br>&nbsp;&nbsp;&nbsp;&nbsp;前面提到，</span><span>Jabber</span><span>会话管理器组件（简称</span><span>JSM</span><span>）处理各种类型的包：消息类型、在线信息类型、查询连接到一个</span><span>Jabber</span><span>主机上的发起者或送达者的</span><span>Jabber</span><span>用户信息。同时，</span><span>JSM</span><span>也处理针对离线用户的数据包。比如，尽管我不在线，你还是通过我的</span><span>Jabber ID(stpeter@jabber.org)</span><span>发了一条消息给我。</span><span>JSM</span><span>将对这条消息进行适当处理，很可能一直保存到我再次上线。</span></p>
<p class=MsoNormal><span>JSM</span><span>通过从</span><span>XML</span><span>流中查找&#8220;资源&#8221;元素（所谓的&#8220;资源&#8221;是指设备、客户端、我的连接所在的位置；可能是&#8220;</span><span>laptop</span><span>&#8221;、&#8220;</span><span>Gabber</span><span>&#8221;、&#8220;</span><span>home</span><span>&#8221;）来判断用户是否在线。通常，如果一个数据包不包含资源元素，表明该用户不在线。但有时资源元素会因为错误而丢失，因此</span><span>JSM</span><span>在肯定用户真的离线后，才发送消息包给&#8220;离线&#8221;组件，&#8220;离线&#8221;组件可能（举例而言）会保存该消息或重新找回一个</span><span>vCard</span><span>。</span></p>
<p class=MsoNormal><span>如果用户在线，消息、在线信息、</span><span>iq</span><span>包不再发送到离线组件，而是由</span><span>JSM</span><span>进行处理。实际上，任何一个包只会有一到两个可能的状态：要么它被转发给用户，要么它由用户发出。因此，</span><span>JSM</span><span>开启两个监听，一个是&#8220;</span><span>to</span><span>&#8221;，一个是&#8220;</span><span>from</span><span>&#8221;，并将它们路由到</span><span>Jabber</span><span>服务器中指定的模块中。一旦指定模块处理完包体，包体将被送回监听程序，以备以后更多模块进行处理，如果所有处理完毕，包体将发送给消息源或消息目的地。</span></p>
<p class=MsoNormal><span>下面这个例子将有助于理解。我收到从</span><span>foobar@jabber.org</span><span>发出的一个消息。我在线，因此消息备送达</span><span>JSM</span><span>。&#8220;</span><span>to</span><span>监听&#8221;监听到有一个包发给我，于是发出一个请求到已经注册到</span><span>JSM</span><span>的模块。第一个响应模块是</span><span>mod_filter</span><span>，该模块按用户指定的标准对进来的消息进行排序。在这个例子中（我好像从来没有从我们的朋友</span><span>foobar</span><span>那里很重要的批评信息），我配置</span><span>mod_filter</span><span>将所有从</span><span>foobar@jabber.org</span><span>发送到我的邮箱的消息通过</span><span>SMTP</span><span>传输器转寄。我们说</span><span>mod_filter</span><span>对消息进行了重新格式化，使得指定接收端现在由</span><span>smtp.jabber.org</span><span>取代原来的</span><span>jabber.org</span><span>，然后将包体发回给&#8220;</span><span>to</span><span>监听者&#8221;。另一个对已注册组件的呼叫上来，单没有任何回应，因此包体被送到</span><span>stpeter@smtp.jabber.org</span><span>，使得包体直接转寄到我的电子邮箱中。</span></p>
<p class=MsoNormal><span>需要着重指出的是这个过程是重复的，所以许多模块都可以在包体完成发送到或来自用户动作之前对包体进行处理。这使得</span><span>JSM</span><span>拥有了极大的弹性和扩展性，因为这样可以在不对</span><span>JSM</span><span>原有模块进行任何改动的基础上，很容易地添加新地模块（只需要对服务器地配置文件进行相应修改即可）。</span></p>
<h3><span><span><font size=5>1.7.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span><span>&nbsp;</span>Threading </span><span>线程</span></font></h3>
<p class=MsoNormal><span>Jabber</span><span>会话管理器通过线程来提高性能。当服务启动时，一定数量地线程被指派到线程池（实际数目由配置文件决定）。当系统其他部分的装载组件反馈消息包给会话管理器时，会话管理器动态地从线程池中取出没有使用的线程，将它们指派给消息端口，这些消息端口正排队等候包体（一个&#8220;消息端口&#8221;表示支持一个客户连接的数据结构）。如果线程池中没有可用的线程，会话管理器可能（但不是必须）创建一个新的线程，并将它指派给指定的消息端口。下面是这个过程的可视化描述：</span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span></span></p>
<p class=MsoNormal><span><o:p>&nbsp;<span><o:p><img height=480 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_5.JPG" width=435 border=0></o:p></span></o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<h3><span><span><font size=5>1.8.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span><span>&nbsp;</span>Delivery Logic </span><span>传送逻辑</span></font></h3>
<p class=MsoNormal><span>传送组件是服务器的核心，因为它将数据从一个基本组件移动动另一个基本组件。这个级别的数据处理逻辑如下图：</span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span></span></p>
<p class=MsoNormal><span><o:p><img height=480 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_6.JPG" width=411 border=0><br>&nbsp;</o:p></span></p>
<p class=MsoNormal><span>一旦一个包体被传送到一个基本组件（接收、连接、执行、装入），它将被发送到一个子组件，类似</span><span>jpolld</span><span>或</span><span>xdb_ldap</span><span>进行进一步处理。</span></p>
<p class=MsoNormal><span>一个预处理的例子可能是一个</span><span>xdb</span><span>（比如一个数据库连接）需要被处理。一个处理条件可以是</span><span>JSM</span><span>中所有有用的路由名字空间的总和。一个传送包体改变的例子可以是消息格式的改变，比如加上传入地址。</span></p>
<h3><span><span><font size=5>1.9.</font><span>&nbsp;&nbsp; </span></span></span><font size=5><span><span>&nbsp;</span>Transports </span><span>传输器</span></font></h3>
<p class=MsoNormal><span>虽然一个健壮的、</span><span>XML</span><span>基础的消息系统结构是</span><span>Jabber</span><span>项目的核心目标，另一个重要的目标是进行消息系统间的协同作业。幸运的是，</span><span>Jabber</span><span>项目通过使它的协议完全开放来实现协同作业。同时，</span><span>Jabber</span><span>项目通过使用</span><span>Jabber</span><span>世界里叫做&#8220;传输器&#8221;的东东来实现</span><span>Jabber</span><span>开放的</span><span>XML</span><span>格式与众多非</span><span>Jabber</span><span>格式间的通信。</span></p>
<p class=MsoNormal><span>当一个</span><span>Jabber</span><span>用户发送消息给一个外部（非</span><span>Jabber</span><span>）系统的用户时，消息的传送包括了一个传输器组件的工作。用户的</span><span>Jabber</span><span>客户端发送一个消息给</span><span>Jabber</span><span>服务器，并指明一个包含外部系统名的</span><span>Jabber ID</span><span>（如</span><span>psaintandre@aim.jabber.org</span><span>），而不是发送给外部</span><span>IM</span><span>系统上的一个用户。接着</span><span>Jabber</span><span>服务器将数据指向指定的传输器应用程序。如果传输器是本地的（在同一台机器上运行），</span><span>Jabber</span><span>服务器直接与它进行通信。如果传输器不在本地运行（在另一台机器上），本地服务器发送一个包给远程服务器，该远程服务器将会把包发送给指定的传输器。一旦传输器接收到</span><span>XML</span><span>包体，它把信息（或指示）&#8220;转变&#8221;成另一个</span><span>IM</span><span>网络可以识别的本地包，并把这个本地包传送到那个</span><span>IM</span><span>网络中。</span></p>
<p class=MsoNormal><span>下面是</span><span>Jabber</span><span>传输器工作的高级概览：</span></p>
<p class=MsoNormal><span><o:p>&nbsp;<img height=480 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_7.JPG" width=391 border=0><br></o:p></span></p>
<p class=MsoNormal><span></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>实际上，一个传输器实现了一个代理模式。大多数传输器拥有自己的小型会话管理器，这个会话管理器将在线信息、消息、（有时）查询信息进行</span><span>Jabber XML</span><span>协议和&#8220;外部的&#8221;（非</span><span>Jabber</span><span>）协议之间的转换。总的来说，当一个用户登陆到</span><span>Jabber</span><span>上，传输器就为和这个用户进行通信创建一个线程。</span></p>
<p class=MsoNormal align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>有时，进行</span><span>Jabber</span><span>协议的转换是很直接的，例如，当一个外部协议是很好的文档化的（比如</span><span>IRC</span><span>协议，即</span><span>AIM</span><span>协议的&#8220;奥斯卡&#8221;版本）。而另外有些时候，对于封闭的或文档的自然协议（如</span><span>Yahoo! Messenger</span><span>协议）进行协议转换就非常困难。人们希望</span><span>IM</span><span>统一化组织（</span><span>(http://www.imunified.org (http://www.imunified.org/))</span><span>）能够成功开放一些现在还是封闭的消息协议，或者至少为这些封闭协议的协议转换创立一套开放的协议。</span></p>
<p class=MsoNormal align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>绝大多数传输器都是为了与非</span><span>Jabber</span><span>服务进行通信，但也有个别例外。比如，群组聊天传输器，这个传输器使得</span><span>Jabber</span><span>用户们可以在一个聊天室里进行聊天，或者以类似</span><span>IRC</span><span>界面的方式进行通信。群组传输器保留每一个房间当前所有用户的记录，并发送每条消息给该房间的所有用户，使得一个房间表现得象一个映射服务器。它根据需要创建和销毁房间，如果我象加入一个不存在得房间，传输器将创建该房间，如果我使最后一个离开房间的用户，传输器将在我离开后销毁这个房间。每一个单一的房间通过类似</span><span>groupname@groupchatserver</span><span>这样的名字进行识别，每一个参与者通过一个对其昵称的唯一描述进行识别。比如，在莎士比亚的《麦克白》中女巫们的&#8220;</span><span>groupchat</span><span>&#8221;可能发生在一个地址为</span><span>cauldron@conference.withces.org</span><span>的房间，女巫们通过类似</span><span>cauldron@conference.withces.org/firstwitch</span><span>的名字进行识别。下面使一个用户可能看到的：</span><span><o:p></o:p></span></p>
<p class=MsoNormal><span><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal><span></span></p>
<p class=MsoNormal><span><o:p><img height=384 src="http://blog.csdn.net/images/blog_csdn_net/kunp/19531/r_8.JPG" width=575 border=0>&nbsp;</o:p></span></p>
<h3><font size=5><span>1.10. Subscriptions </span><span>订阅</span></font></h3>
<p class=MsoNormal><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>一个</span><span>Jabber</span><span>实体可以订阅其他</span><span>Jabber</span><span>实体（如：任何和一个</span><span>Jabber ID</span><span>关联的事物）的在线信息，一个订阅本质上是被订阅者同意发送在线状态改变给订阅者。这个信息同时存储在订阅者和被订阅者的名单中。当我通过认证并在服务器上创建一个会话，我的在线信息被存放到</span><span>Jabber</span><span>会话管理器中。当我改变我的在线状态时，</span><span>&lt;presence/&gt;</span><span>包将被服务器处理，服务器在我的名单中进行查询，并将在线信息状态包发送给所有订阅我的在线状态的</span><span>Jabber</span><span>实体。订阅包括一下几种类别，这些类别存放在包含实体的名单上：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>to</span><span>――另一个发送在线状态信息给你的实体</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>from</span><span>――另一个从你这里获得在线状态信息的实体</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>both</span><span>――另一个发送再现信息状态给你，又从你这里获取在线信息状态的实体</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>none</span><span>――即不从你这里获取再现信息状态，又不发送在线信息状态给你的实体</span></p>
<p class=MsoNormal><span>发送在线状态信息的实体并不一定是另一个</span><span>Jabber</span><span>用户，它也可以是一个外部的服务，比如一个数据流或一个非</span><span>Jabber</span><span>的</span><span>IM</span><span>系统。在后面的例子中，非</span><span>Jabber</span><span>系统的用户订阅通过一个传输器解决，</span><span>Jabber</span><span>用户注册到指定传输器（如：</span><span>icq.jabber.org</span><span>），以便将在线状态信息传送给非</span><span>Jabber</span><span>系统的用户。一旦</span><span>Jabber</span><span>用户成功注册，传输器就需要知道该用户什么时候上线，因此，它发送一个在线状态信息订阅请求给该用户。一个特殊的带有&#8220;</span><span>from</span><span>&#8221;特性的在线状态信息订阅数据包从传输器产生并发送，其中的数据必须可以登录到本地协议。</span></p>
<p class=MsoNormal><span>Jabber</span><span>服务器包含一个所有用户的订阅信息组成的名单（该名单通常直接存放与文件系统中，尽管这些信息一个可以存放在数据库中）。这个名单被命名为花名册，很像其他</span><span>IM</span><span>系统中的&#8220;好友列表&#8221;。</span><span>Jabber</span><span>的花名册存放在服务器上，这样用户就可以自由的从一个地方到另一个地方，从一台计算机到另一台计算机自由的调用它。</span><span>Jabber</span><span>服务器根据用户意愿对花名册上的对应订阅关系进行允许、拒绝等操作。花名册还包括一些用户特殊的其它信息，比如用户的昵称，以及用户所属的群组。这些信息可以通过客户端调用适当接口显示花名册时显现出来。</span></p>
<h3><font size=5><span>1.11.<span>&nbsp; </span>Jabber IDs Jabber</span><span>代号</span></font></h3>
<p class=MsoNormal><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在</span><span>Jabber</span><span>里，有许多不同的实体需要进行相互通信。这些实体可以表现为传输器、群组聊天室、或者是单一的</span><span>Jabber</span><span>用户。</span><span>Jabber IDs</span><span>是内外结合的表示用户身份或路由信息。</span><span>Jabber IDs</span><span>的关键特性包括：</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>它们唯一确定进行即时消息和在线信息状态通信的独立对象或实体</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>用户很容易记住它们并在真实世界中进行表达</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>它们很灵活，以至于可以包容其它</span><span>IM</span><span>和在线信息状态表。</span></p>
<p class=MsoNormal><span>每一个</span><span>Jabber ID</span><span>（或</span><span>JID</span><span>）包括一套有序的元素。</span><span>JIDs</span><span>由域、节点、数据流格式的资源组成：</span></p>
<p class=MsoNormal><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>[node@]domain[/resource]<o:p></o:p></span></p>
<p class=MsoNormal><span>Jabber ID </span><span>元素有以下定义：</span><span><o:p></o:p></span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>域名是第一标识符。它表明实体连接的</span><span>Jabber</span><span>服务器。每一个可用的</span><span>Jabber</span><span>域名都应拥有一个完整的域名。</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>节点是第二标识符。它表明&#8220;用户&#8221;本身。所有的节点都对应一个精确的域。不过，节点是可选的，一个精确的域（如</span><span>conference.jabber.org</span><span>）是非法</span><span>Jabber ID</span><span>。</span></p>
<p class=MsoNormal><span><span>?<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>资源是可选的第三标识符。所有资源都属于一个节点。在</span><span>Jabber</span><span>中，资源被用来识别属于用户的特殊对象，比如设备或位置。资源是一个单独的用户可以同时拥有几个与同一</span><span>Jabber</span><span>服务器的连接；如：</span><span>juliet@capulet.com/balcony vs. juliet@capulet.com/chamber. </span></p>
<p class=MsoNormal><span>一个</span><span>Jabber</span><span>用户通常通过一个特殊的资源与服务器相连，因此在连接时有一个</span><span>node@domain/resource</span><span>形式的地址（如</span><span>juliet@capulet.com/balcony</span><span>）。由于资源时会话性的，用户的地址可以和类似</span><span>node@domain</span><span>（如</span><span>juliet@capulet.com</span><span>）进行通信，就象人们使用和它相同的形式的</span><span>email</span><span>一样。</span></p>
<p class=MsoNormal><span>注意虽然在有些情况下，消息可以直接发送到一个精确资源，但总的来说，一个发往</span><span>juliet@capulet.com</span><span>消息根据</span><span>Jabber</span><span>服务器上的规则进行路由，因为每一个连接实例都有它自己的优先级设定。这样，如果一条消息正好是发送给</span><span>juliet@capulet.com</span><span>（即没有指定任一资源），该消息路由到拥有最高优先级的资源，如：</span><span>juliet@capulet.com/balcony</span><span>。</span></p>
<h3><font size=5><span>1.12. Server Dialback </span><span>服务器回滚</span></font></h3>
<p class=MsoNormal align=left><span>1.2</span><span>版的服务器增加了一个成为服务器回滚的功能。这个功能是设计用来服务器欺骗的，这样就为服务器－服务器之间的交互增加了一个额外的安全方法。关于这个功能的详细信息会在这个文档的未来版本中提供。下面网址提供了一些初步的文档：</span><span><a href="http://docs.jabber.org/draft-proto/html/dialback.html">http://docs.jabber.org/draft-proto/html/dialback.html</a>.</span><span><o:p></o:p></span></p>
<h3><font size=5><span>1.13. Conclusion </span><span>结束语</span></font></h3>
<p class=MsoNormal><span>本文档提供了一个</span><span>Jabber</span><span>系统结构的高阶的概述。如果你对本文档有什么疑问，请直接通过</span><span>stpeter@jabber.org</span><span>以</span><span>email</span><span>或</span><span>Jabber</span><span>与文档的作者（</span><span>Peter-Saint-Andre</span><span>）进行联系。</span></p>
<h3><font size=5><span>1.14. Copyright Information </span><span>版权信息</span></font></h3>
<p class=MsoNormal align=left><span>This document is copyright 2001 by Peter Saint-Andre.<o:p></o:p></span></p>
<p class=MsoNormal align=left><span>Permission is granted to copy, distribute and/or modify this document under the terms<o:p></o:p></span></p>
<p class=MsoNormal align=left><span>of the <i>GNU Free Documentation License </i>(http://www.gnu.org/copyleft/fdl.html),<o:p></o:p></span></p>
<p class=MsoNormal align=left><span>Version 1.1 or any later version published by the Free Software Foundation, with no<o:p></o:p></span></p>
<p class=MsoNormal align=left><span>Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You may obtain a<o:p></o:p></span></p>
<p class=MsoNormal align=left><span>copy of the GNU Free Documentation License from the Free Software Foundation by<o:p></o:p></span></p>
<p class=MsoNormal align=left><span>visiting <i>http://www.fsf.org/ </i>or by writing to:<o:p></o:p></span></p>
<p class=MsoNormal align=left><span>The Free Software Foundation, Inc.<o:p></o:p></span></p>
<p class=MsoNormal align=left><st1:address><span>59 Temple Place</span></st1:address><span> - <st1:address>Suite 330</st1:address><o:p></o:p></span></p>
<p class=MsoNormal align=left><span>Boston</span><span>, MA 02111-1307</span><span><o:p></o:p></span></p>
</div>
<img src ="http://www.cnblogs.com/maxpain/aggbug/421424.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48002/" target="_blank">竞争日趋激烈 微软欲借 Windows 7 扭转战局</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>为爱奔跑--＜豪杰春香＞</title><link>http://www.cnblogs.com/maxpain/archive/2006/04/07/369420.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Fri, 07 Apr 2006 08:17:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2006/04/07/369420.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/369420.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2006/04/07/369420.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/369420.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/369420.html</trackback:ping><description><![CDATA[<div>我是怎么了？我一直在问自己。这两天我这个&#8220;奔3&#8220;的人竟然被一部电视剧给迷住了，而且是一部我向来都不屑的韩剧。这部韩剧就是《豪杰春香》，一部简单可爱的片子。</div>
<div>&nbsp;</div>
<div>如果说剧情，那很简单，简单的如同大多数的爱情剧一样讲述了一对青年恋人相识、相知、相爱的过程以及其间的磨难。剧中男主人公梦龙奔跑的镜头特别的多，在春香离开他之后依然保持着10分钟跑回家的习惯。一个人的习惯多是因为一些特别的影响，比如最爱的人，比如奋斗的目标。人物奔跑的力度和人物的感情纠结在一起，不由得让人们想起《罗拉快跑》里面的那句台词&#8220;你说过，爱情可以解决一切&#8221;。</div>
<div>&nbsp;</div>
<div>因为一个爱字人人都在奔跑，为爱而奔跑的人是世界上最可爱的人，他可以不在乎别人的眼光，包括梦龙穿着笔挺的西装奔跑，我不会感到诧异，因为他是充满欢喜的奔跑，如果幸福就在前面，再苦再累也是无所畏惧的。</div>
<div>&nbsp;</div>
<div>其实我看到这些奔跑的镜头，委实有些感动。比起无数的山盟海誓一个速度上的简单距离让我们想到了很多，感受到很多。但是更让我感动的不是梦龙的奔跑，而是春香的奔跑。其实春香一直在努力的奔跑，只不过不是一段可以实际衡量的距离而已。春香面对着各种误解、非议乃至威胁，她都是倔强的努力着，尽力掩盖自己内心的痛苦，尽力在爱人面前微笑，以这种方式、这种谅解、这种姿态来奔跑，期望能到到达幸福的终点。这才是这部韩剧最感动我的地方--为爱奔跑。</div>
<div>&nbsp;</div>
<div>爱情是折磨人的，二号男主人公卞学道为了春香做了许多不可理喻的事情。剧中他的万般怜惜都表现在面上，怀里是自己的深爱的女子，但是她只爱眼前的那个男人，而那个男人正与别个女子拥吻，此情此境，情何以堪。我不由得想起很早前看过的一场辩论会上的辩词，一方说：&#8220;得到不是最快乐的事情，最快乐的事情应该是追求的过程&#8221;，对方辩手立刻反驳道：&#8220;如果一个下着大雨的夜晚，你焦急的等待着公车到来。当你看到公车过来是你不顾大雨和泥泞追了上去，但是车没有停，那一刻请问你感受到追求的快乐了吗？&#8221;。是啊，追求的过程当中有快乐更有痛苦，只是我们不要在痛苦中迷失就好，卞学道我想他是迷失了。</div>
<div>&nbsp;</div>
<div>此外这个片子的主人公春香我非常喜欢，因为她让我想起了另外一个人--10几年前的那部《东京爱情故事》中的赤名莉香，那部当时我看了11遍的爱情剧给我留下的印象就是莉香阳光般灿烂的微笑。我想我是有个结的，一是不满于《东京爱情故事》分手的结局，一是期望找一个莉香一样的女朋友。</div>
<div>&nbsp;</div>
<div>现在《豪杰春香》给了我一个美满的大结局，而像莉香一样的女孩也已经成为我的妻子。所以在看完这部片子的时候，我笑了，因为我觉得幸福。</div>
<img src ="http://www.cnblogs.com/maxpain/aggbug/369420.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48001/" target="_blank">上海电信计划2012年80%用户实现100M带宽</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>《战争之王》--如何让其他11个人也有枪</title><link>http://www.cnblogs.com/maxpain/archive/2006/04/06/368285.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Thu, 06 Apr 2006 05:26:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2006/04/06/368285.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/368285.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2006/04/06/368285.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/368285.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/368285.html</trackback:ping><description><![CDATA[看到导演和演员列表我就决定要看这部片子了，导演中的怪才安德鲁.尼科和演员中的天才尼古拉斯.凯奇。尼古拉斯.凯奇，我很喜欢这个家伙，他的许多动作片都堪称经典，不过在他塑造的众多角色我更加喜欢小聪明的角色，比如《火柴人》，还有这部片子。至于导演安德鲁可是导过《楚门的世界》。
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;全世界一共有5亿零五万支军火，那就是说每12个人就有1支枪 唯一的问题是，我们如何让其他11个人也有枪&#8221; ，这个开头一下子吸引了我，至少告诉我这不是一部好莱坞的烂片。影片讲述了一个移民到美国的苏联人尤里的发家史，所不同的是他做的是军火买卖。每天出入于《卢旺达饭店》那样的杀戮世界，周旋于各中势力之间，在他最后成功的时刻他却感到了不安，失去了弟弟、妻子和孩子。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 整部片子透着一股子黑色幽默，但是却又让你感觉到那么的真实，带着你去看透影片中的各种人，战争受害者、小军火贩子、大军火贩子、战争发动者、无奈的警察、腐败的军人还有幕后的政客。如果说《千层糕》将贩毒生涯秀出交响乐般的优雅，《战争之王》则是把军火交易拍成了史诗。我一直觉得尤里是个好人，军火交易也只是买卖而已，只不过是特殊的买卖，就像尤里说的&#8220;美国总统才是全世界最大的军火商，他需要我们。&#8221;。真实的就是真实的，那些什么正义原本就不过是个说辞。电影中你很容易看到各种武装份子，他们所在的组织和国家挑起了战争，然后从尤里的手中买过武器，再交给这些&#8220;正义&#8221;的人去杀戮。尤里这样的军火商人推动了战争，让杀戮变得更加的普遍和残酷，但是没有枪支人类还是一样互相残杀，用钢铁和青铜的冷兵器去杀死对方，再早些可以用石头木棒。人类一开始就有战争和厮杀吗？据说在最早的时候人类是同严苛的自然环境作斗争，但是谁知道呢？至于我，还是相信人类一开始就是有战争的。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这部影片当中的镜头非常真实，那时因为里面确实都是真家伙，由军火贩子赞助的。片中使用超过3000把AK-47真枪，因为道具枪要比真枪贵（确实是这样），而且军火走私商的效率比制片公司要好。其中一个场景整排的坦克，那是属于一个捷克籍军火商的，拍摄该场景前通知了NATO（北大西洋公约组织）以避免不必要的误会，而且这些坦克只能借拍摄组拍摄到年底，因为那个时候这些坦克要买给利比亚了。</div>
<div>&nbsp;</div>
<div>此外这部电影当中很多台词非常经典：</div>
<ul>
    <li>在这世界上一共有伍亿伍千万支军火在流通，那就是说每十二个人就有一支枪，唯一的问题是：我们如何让其他十一个人也有枪？(如何把武器卖给那另外的十一个人)
    <li>第一次卖枪的感觉，很大程度上就象第一次做爱一样，你根本不知道自己在干嘛，虽然很刺激，但很快就结束了。
    <li>我的一生都想远离暴力，但其实我更该接近它才对，这是人类的天性，人类最早遗骸的胸腔中，就有矛头。
    <li>-- Already more of those in America than McDonald's. <br>-- 怎么，你要开个枪店？ <br>-- 美国的枪店已经比麦当劳还多了。
    <li>先生，有没兴趣看看肩扛式SA-7地对空导弹，中国造，过时的型号，用在现代军事战机上或许不行，不过打打商业客机，还是挺不错的。
    <li>除了"救世军"，我卖武器给任何一支军队，我把以色列造的乌兹冲锋枪卖给回教徒，我把共产主义造的子弹卖给法西斯，我还把军火运到阿富汗，尽管他们在打我的苏联盟军，我从来没卖给过奥萨姆&#183;本&#183;拉登，不是因为道德问题，而是那时他的支票总是跳票。
    <li>-- 我不信你回去只是要卖百事可乐，这是你希望被后人纪念的方式吗？ <br>-- 我根本不希望留名青史，如果是那样，就表示我已经死了。
    <li>卖车的会告诉你开车有危险？卖香烟的会告诉你吸烟会死？每年他们的商品比我的害死更多人，起码我的商品还有个保险栓呢。
    <li>每一个在非洲的派系，喜欢给自己起一些高尚的名字，解放这个，爱国那个，或者共和或民主什么的，也许他们不知道自己是什么，地球唯一喜欢战争的民族，往往最残忍的暴行都出自声称自己为自由战士的人们。
    <li>让我来告诉你将会发生什么，这样可以让你有所准备，很快，会有人来敲门，你会被叫到外面去，在过道里，会有一个官阶比你高的人站在那里，首先，他会祝贺你所做的一切，你使世界成为一个和平的地方，你会得到嘉奖或升职，然后他会告诉你，我需要被释放，你会反对，你也许会以辞职来要挟他，但是在最后，我会被释放，我被释放的原因，和你认为我会被判刑的原因是一样的，我和一些世界上称自己为领导人的人打交道，这些人当中有一些人是你的敌人的敌人，世界上最大的军火交易商是你的老板，美国的总统，他一天卖的，比我一年卖的还多，有时，在枪支上找到他的指纹是一件很尴尬的事，有时，他需要像我这样的自由工作者，来支持那些他不方便支持的军队，所以，你称我为恶魔，但不幸的是，对你，我是一个必须要存在的恶魔。
    <li>尽管军火走私愈演愈烈，世界上最大的军火供应商却是美、英、俄、法、中，他们同时也是联合国安理会五个常任理事国。 </li>
</ul>
<img src ="http://www.cnblogs.com/maxpain/aggbug/368285.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48000/" target="_blank">数万名网友签名抗议星际争霸2取消局域网功能</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>2005年的第一场暴力--《最恶都市》</title><link>http://www.cnblogs.com/maxpain/archive/2005/07/07/188139.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Thu, 07 Jul 2005 13:14:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2005/07/07/188139.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/188139.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2005/07/07/188139.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/188139.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/188139.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对《罪恶之城》的期待，几乎类似于对这个春天干涸的北美票房榜上第一场雨的盼望。熟悉这位顽童般的拉丁裔导演罗伯特&#183;罗德里格兹的影迷会知道，他和去年制造《杀死比尔》的昆汀&#183;塔伦蒂诺一样，醉心于出产cult电影；而早早现身影院和电视广告的预告片里面，黑白影像的另类和人物的狂放被放大到了极致，会让我们认为，这又是一部风格先行的<!--NEWSZW_HZH_BEGIN--><!--NEWSZW_HZH_BEGIN--> <!--NEWSZW_HZH_END--><!--NEWSZW_HZH_END-->电影：一如好玩的《特工小子》，一如浪漫火爆的《杀人三部曲》和《墨西哥往事》系列，追捧和制造着娱乐的流行元素。然而事实证明，《罪恶之城》系列漫画原作者弗兰克&#183;米勒参与的本片，根本拒绝对大众的欣赏口味做出一丝妥协：风格大胆的影像固然带来了观影时的巨大冲击力，更让我们胆寒的是这场如期而至的雨水黑色得带着浓烈的腥气。罪恶之城，天空永恒黑暗，飞雪的降临无法遮盖污水四溢的街道。残酷吗？电影甚至不想给我们哪怕一次深呼吸喘口气的机会<STRONG>。<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这部电影，不是任何一个中庸的创作者可以完成的作品。相信没有大胆的原作者弗兰克&#183;米勒的暴力漫画，没有抱着同样宣泄心理(用电影来宣泄对于社会美好价值沦丧和幻灭的无情讽刺)的嬉笑怒骂的电影疯狂青年罗伯特&#183;罗德里格兹和昆汀&#183;塔伦蒂诺(昆汀的名字赫然列在嘉宾导演一栏上面)。它也绝对不会换来影迷和观众中庸的反应，你要不在看到电影的一刻狂飙突进式的喜欢上它，要不坚决的唾弃为垃圾，完全不可能存在一种温和的态度。 
<CENTER class=f12><A href="http://ent.sina.com.cn/m/f/f/sincity/index.html" target=_blank><STRONG><IMG alt=焦点影评：《罪恶之城》2005年的第一场暴力 src="http://image2.sina.com.cn/ent/r/i/2005-04-07/U1117P28T3D695886F329DT20050407101834.jpg" border=1></STRONG></A><BR></CENTER><BR>
<P><STRONG>　　</STRONG>不错，从始至终让我们迷醉的，首先是本片风格至死的环境设计和镜头表现，可以说是迄今为止看到的电影对于漫画这种艺术最好的还原：强烈的黑白两色带来的对比，加上镜头的调度，光线的角度，很多镜头都可以单独出来作为漫画的一个独立的画面来欣赏，尤其黑色在影片中的使用层次丰富，表现出了&#8220;罪恶之城&#8221;本来的面目，甚至达到了彩色电影无法取得的影像质感；而电影这一艺术形式在动作场面上的优势和色彩运用上的自如，则是优于漫画的方面，直接导致了视觉上凌厉的效果。影片大多数时候都是黑白两色，唯有迸溅而出的鲜血殷红惨烈，使得人物的鲜血披面尤其触目惊心。同时一切与爱情和美丽相关的联想再次使得红色成为了编导的宠儿，于是电影里面出现了女人洁白肌肤上红色晶莹的晚礼服，闪烁一如天空美丽的繁星；还有红色丝绒心型大床上金色头发的年轻女人，代表了一种纯洁和爱的欲望。电影当中类似的灵感层出不穷：吸烟后变得更加幽深的绿色眼眸，黄色的恶棍散发出来的腐烂气息，一直不断的带给观众尖锐的视觉冲击。同为全蓝幕拍摄的电影，这一部《罪恶之城》显然在与去年的《天空上尉和明日世界》的比较中更占上风。无他，只因为这个电影里面的人物和故事更适合以一种肆无忌惮，虚实莫辨的方式生存。而暴力宣泄的毫不顾忌则和场景的不真实相得益彰，形式上的奇突更衬托出内容上剔骨见肉的黑色元素，很多时候，我们都能感受到类似《低俗小说》里面极端暴力带来的极端寒意。</P>
<P><STRONG>　　</STRONG>电影采用了原作漫画的三段故事，中心人物分别是生活在&#8220;罪恶之城&#8221;的三个男性：警察哈提根(布鲁斯&#183;威利斯饰)、警察马沃(米奇&#183;洛基饰)和刚刚退休的社会新闻记者德维特(克里夫&#183;欧文饰)。复述剧情对于这部电影而言并没有太大的意义，我们几乎不需要知道他们存在的理由和暴力的过程。第一段故事的追踪凶手，第三段故事中看似简单的人物关系，矛头都指向支配着这个城市的黑帮和高层人物。表面上被暴力支配着的三个男人，尤其是登场不久的身形巨大脾气暴躁的警察马沃，杀人不眨眼，似乎不折不扣是暴力的化身。但随着更冷血的吃人者的出现(电影当中这个角色被设置成具有动物的某些行为，显然寓意兽性，更暗示了对于这个黑暗城市控制者的无情否定)，以及他身后的统治者势力显露出来的令人齿冷的真面目，我们才发现，这个城市最浓重的黑暗不在于赖以生存的街头暴力，在于被侵蚀的庞大的所谓城市秩序。当全片中的警察系统不过是掌握权力者满足变态目的的武器，什么正义，什么良心，都敌不过金钱驱使下无休止的欲望。代表宗教圣洁的教士虚伪的外表下是无耻的勾当(这个角色的扮演者正是原漫画作者弗兰克&#183;米勒)，相反电影中最纯洁的一个形象，除了纯真的小女孩南希，就是和马沃短暂相逢随即死去的妓女戈蒂(杰米&#183;金饰)，她在红色的丝绒下完美无瑕的肉体，金发散发出的真纯的光芒，都无比耀眼夺目。这也是对于&#8220;罪恶之城&#8221;本质最直接的揭露。</P>
<P>　　影片充斥着大量超现实的细节。即使不提那些刻意设计的处于虚幻与真实之间的街景，第二段故事里面警察杰基男孩(本尼西奥&#183;德托罗饰)死去的经过以及此后发生在郊外和城市街道上的枪战，出现了大量在正常电影中会被视为极端不合理的情节：白线般的雨柱，杰基男孩去捡自己被砍断的手，死去多时的他在车中的喋喋不休。这一段情节拍得极其有张力，伴随着记者德维特混乱而迷茫的旁白，和银幕上仿佛永远不会停歇的雨，从杰基男孩与德维特的邂逅，到最终街头的大决战，故事的音越拔越高，到最后随着子弹的扫射终于嘈嘈切切如急雨般倾泻而出，戏剧性的高潮在暴力的宣泄下带着人物内心的空虚和悲哀。就在那一刻，无数年轻女孩在屋顶的高处扫射，电影的不真实和荒谬感也达到了顶点：她们让我想起了《杀死比尔》里面那些随意而匪夷所思的暴力镜头，如果说《比》片是一种道德的幻灭，本片在表现这一点上面分毫不差，而且应该加上人生存的环境本身的扭曲和疯狂。片中暴力镜头更多的不是让人愤怒，而是充满了蚀骨的寒冷。伊利亚&#183;伍德出场的一段和他的结局，都使人在心理上有极度的不舒适感(不知道有多少女性观众能够撑过这段情节)。而他居住的那个农庄，在电影中多次出现，地下室的场景几乎就是地狱的翻版。虽然编导们点到即至，甚至采用了剪影这样的艺术手段来避开最血腥的场景，但绝不妥协的暴力表现，本片可以说是《杀死比尔》后的又一个高峰。</P>
<CENTER class=f12><A href="http://ent.sina.com.cn/m/f/f/sincity/index.html" target=_blank><STRONG><IMG alt=焦点影评：《罪恶之城》2005年的第一场暴力 src="http://image2.sina.com.cn/ent/r/i/2005-04-07/U1117P28T3D695886F328DT20050407101834.jpg" border=1></STRONG></A><BR></CENTER><BR>
<P><STRONG>　　</STRONG>赤裸的暴力和非常不真实的细节，才能真正刻画&#8220;罪恶之城&#8221;本身的疯狂。而影片中唯一剩下的一点黑色天空里的亮点，是几位男性角色和他们关心的人之间卑微而艰难的爱，这也是唯一值得期待的东西。电影贯穿了三个男人的旁白，当他们杀人的时候，鲜血沾满了双手的时候，或者平静或者迷茫或者沉痛的声音里头，始终都没有忘记那个曾经让他们活得象个人的女人。金色头发的纯洁光芒，那双清澈无瑕的眼睛，还有象豹子一样野性敏捷的脸，她们是遗留在这个邪恶城市里面唯一值得付出的东西。</P>
<P>　　电影一直陷入在一种停不下来的状态当中，就象乌玛&#183;瑟曼在《杀死比尔》当中无法停止的复仇之旅，罪恶之城中疾风骤雨般的暴力，最终把我们和人物一起送到了嘎然而止的结局。那一刻，雪花静静飘落，此前的所有子弹和鲜血，生命和杀戮，换来的竟然是无奈的哀挽。不堪忍受吗？电影里面的人们并不这样认为，因为那就是这个城市最真实的面目：&#8220;老男人死去了，年轻的女孩子活下来&#8221;，坚韧而艰难的活下来。 <BR><BR>《罪恶都市》剧情超级复杂，因为它不是讲一个故事，而是通过三个故事来表现一个地方和那里的居民。所以我们干脆不谈故事只说人物，因为《罪恶都市》很可能是今年明星最多的电影，里面还有几位相当值得介绍的新面孔。</P>
<P>　　<STRONG>马沃(米基&#183;洛克饰)<BR></STRONG>马沃是本片第一条故事线的主角，一个过气的拳击手。他跟一个叫歌迪的女人发生了一夜情，就在他第一次感受到&#8220;爱&#8221;的时候，她突然被杀。马沃决心复仇。扮演马沃的米基&#183;洛克(MickeyRourke)也是好莱坞有名的叛逆小子，此人在上世纪80年代曾是冉冉升起的硬派小生，主演过《体热边缘》《天堂之门》等片。后来他突然决定放弃电影，转行去打职业拳击(当然，成绩不算理想)。等上世纪90年代末他从职业拳击界退役的时候，已经成了一个满脸沧桑、默默无闻的配角。让他演曾经沧海的拳击手，再合适没有了。</P>
<P><STRONG>　　歌迪(杰米&#183;金饰)</STRONG></P>
<P><STRONG>　　</STRONG>令马沃为之疯狂的美女，美国名模，上过无数时尚杂志的封面。</P>
<P><STRONG>　　凯文(伊利亚&#183;伍德饰)</STRONG></P>
<P><STRONG>　　</STRONG>下令杀死歌迪的黑帮头子，居然由《指环王》中善良的哈比人&#8220;弗罗多&#8221;伊利亚&#183;伍德来演，真是奇特的选角思路。伊利亚&#183;伍德觉得这是他演得最兴奋的角色，惟一遗憾的是弗兰克&#183;米勒并不想要一个嚣张跋扈的&#8220;老大&#8221;，他在片场总是不停地提醒伍德：&#8220;冷静，演得再冷静点。&#8221;</P>
<P><STRONG>　　德维特(克利弗&#183;欧文饰)</STRONG></P>
<P><STRONG>　　</STRONG>德维特是第二条故事线的主角，他是刚刚退休的社会新闻记者，是《罪恶都市》的妓女们惟一信赖的人。现在妓女们跟黑帮发生冲突，导致一个警察死亡。他只好重新回到那些穷街陋巷，尽力斡旋。扮演者是英国演员克利弗&#183;欧文，他刚刚凭<STRONG>《Closer》</STRONG>获得了金球奖最佳男配角奖，不过他最为中国观众熟悉的形象还是&#8220;亚瑟王&#8221;和吴宇森导演的宝马广告。</P>
<P><STRONG>　　盖儿(罗莎丽诺&#183;道森饰)</STRONG></P>
<P><STRONG>　　</STRONG>盖儿是德维特的搭档，《罪恶都市》妓女们的大姐。罗莎丽诺&#183;道森上一个角色是《亚历山大》中的亚历山大大帝之妻，现在摇身一变，成了穿性感皮衣，拿着乌兹冲锋枪打打杀杀的黑道大姐大。</P>
<P><STRONG>　　谢莉(布里特妮&#183;莫菲饰)</STRONG></P>
<P><STRONG>　　</STRONG>谢莉是弗兰克&#183;米勒最喜欢的角色，她是一个在黑白两道面前都游刃有余的女招待，她是串联起三个故事的角色，也是德维特故事里的关键人物。扮演这个角色的布里特妮&#183;莫菲曾是上世纪90年代青春片热潮中出现的小偶像之一，后来成功转型为实力派演员，在《八英里》《黑皮书》等片中的表现可圈可点<STRONG>。</STRONG></P>
<P><STRONG>　　杰基小子(本尼西奥&#183;德尔&#183;托罗饰)</STRONG></P>
<P><STRONG>　　</STRONG>第三个故事的主角，是一个腐败的警察。有趣的是，扮演者本尼西奥&#183;德尔&#183;托罗最著名的形象之一就是《毒网》中正直的墨西哥警察。</P>
<P><STRONG>　　哈提甘(布鲁斯&#183;威利斯饰)</STRONG></P>
<P><STRONG>　　</STRONG>布鲁斯&#183;威利斯是本片中惟一没有被颠覆的演员，他扮演的是《罪恶都市》中惟一的正直警察哈提甘，在即将退休的时候，为了保护一个11岁的小女孩，不惜得罪参议员的儿子&#8220;黄混蛋&#8221;，因此结下纠缠不清的仇恨。布鲁斯&#183;威利斯会在这种大群戏中演个配角，因为他也是弗兰克&#183;米勒的书迷。</P>
<P><STRONG>　　黄混蛋(尼克&#183;斯塔尔饰)</STRONG></P>
<P><STRONG>　　</STRONG>扮演这个坏蛋的，是《终结者3》的男主角尼克&#183;斯塔尔。</P>
<P><STRONG>　　南茜(杰西卡&#183;阿尔巴饰)</STRONG></P>
<P><STRONG>　　</STRONG>当年被布鲁斯&#183;威利斯保护的小女孩后来变成了《罪恶都市》的红舞女郎，扮演者杰西卡&#183;阿尔巴是2005年最有希望蹿红的新人，她因在电视剧《末世黑天使》中扮演虚拟人而出名，今年有份主演三部好莱坞大戏，除了《罪恶都市》，还有漫画电影《梦幻四人组》(TheFantasticFour)和动作片《杀入深海》(Into The Blue)。</P>
<P><STRONG>　　米荷(戴文&#183;奥基饰)</STRONG></P>
<P><STRONG>　　</STRONG>盖儿手下的沉默女杀手，扮演者戴文&#183;奥基是英国名模，最近转战演艺界，立刻成为风头很猛的新晋女星，作品包括《速度与激情2》和刚刚上映的《DEBS》。她在片中用的武士刀由昆汀&#183;塔伦蒂诺赞助，就是《杀死比尔》中乌玛&#183;瑟曼用的那把。</P><!--NEWSZW_HZH_BEGIN--><!--NEWSZW_HZH_BEGIN--><img src ="http://www.cnblogs.com/maxpain/aggbug/188139.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47999/" target="_blank">Silverlight打造杰克逊纪念专题</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>appfuse中集成valuelist</title><link>http://www.cnblogs.com/maxpain/archive/2005/07/03/185394.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Sun, 03 Jul 2005 04:16:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2005/07/03/185394.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/185394.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2005/07/03/185394.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/185394.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/185394.html</trackback:ping><description><![CDATA[<P>将valuelist.jar加入lib中<BR></P>
<P>在appfuse\lib\lib.properties中添加如下内容：</P>
<P>#<BR># ValueList<BR>#<BR>valuelist.version=0.1.7<BR>valuelist.dir=${lib.dir}<BR>valuelist.jar=${valuelist.dir}/valuelist.jar<BR>将properties.xml中添加：<BR>&lt;!-- Web --&gt;<BR>&lt;path id="web.compile.classpath"&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${dist.dir}/${webapp.name}-dao.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${dist.dir}/${webapp.name}-service.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${struts.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${strutsmenu.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${displaytag.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${jakarta-oro.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-digester.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-logging.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-beanutils.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-collections.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-fileupload.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-lang.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-validator.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${servletapi.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${valuelist.jar}"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp; &lt;fileset dir="${javamail.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;fileset dir="${spring.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;fileset dir="${jstl.dir}/lib" includes="jstl.jar"/&gt;<BR>&lt;/path&gt;</P>
<P>在builde.xml中添加：<BR>&lt;war destfile="${webapp.dist}/${webapp.war}"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; webxml="${webapp.target}/WEB-INF/web.xml" compress="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${webapp.target}" excludes="**/web.xml,**/*-resources.xml"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;metainf dir="${webapp.dist}" includes="context.xml"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classes dir="${build.dir}/web/classes"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;exclude name="**/database.properties"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/classes&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${dist.dir}/${webapp.name}-dao.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${dist.dir}/${webapp.name}-service.jar"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;webinf dir="${struts.dir}" includes="*.xml"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;webinf dir="web/WEB-INF" includes="*-resources.xml"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${clickstream.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${struts.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${jstl.dir}/lib"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="jstl.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="standard.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/lib&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${javamail.dir}" includes="*.jar"/&gt;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${log4j.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${strutsmenu.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${valuelist.jar}"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${displaytag.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${hibernate.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${hibernate.dir}/lib"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="odmg*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="dom4j*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="cglib*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="ehcache*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="oscache*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/lib&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${spring.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${sitemesh.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${velocity.dir}" includes="*.jar"/&gt;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${urlrewrite.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/war&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/target&gt;</P><img src ="http://www.cnblogs.com/maxpain/aggbug/185394.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47998/" target="_blank">传诺基亚正在开发Android手机</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>揭开极端编程的神秘面纱: 测试驱动的编程</title><link>http://www.cnblogs.com/maxpain/archive/2005/06/15/174750.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Wed, 15 Jun 2005 03:09:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2005/06/15/174750.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/174750.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2005/06/15/174750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/174750.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/174750.html</trackback:ping><description><![CDATA[<SPAN class=atitle2>通过在编写代码之前编写测试使一切简单</SPAN></ABSTRACT-EXTENDED>
<P>最近 50 年来，测试一直被视为项目结束时要做的事。当然，可以在项目进行之中结合测试，测试通常并不是在 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">所有</I>编码工作结束后才开始，而是一般在稍后阶段进行测试。然而，XP 的提倡者建议完全逆转这个模型。作为一名程序员，应该在编写代码 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">之前</I>编写测试，然后只编写足以让测试通过的代码即可。这样做将有助于使您的系统尽可能的简单。 </P>
<P><A name=1><SPAN class=atitle2>先编写测试</SPAN></A><BR>XP 涉及两种测试： <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">程序员测试</I>和 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">客户测试</I>。测试驱动的编程（也称为 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">测试为先</I>编程）最常指第一种测试，至少我使用这个术语时是这样。测试驱动的编程是让 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">程序员测试</I>（即单元测试 ― 重申一下，只是换用一个术语）决定您所编写的代码。这意味着您必须在编写代码之前进行测试。测试指出您 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">需要</I>编写的代码，从而也 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">决定</I>了您要编写的代码。您只需编写足够通过测试的代码即可 ― 不用多，也不用少。XP 规则很简单：如果不进行程序员测试，则您不知道要编写什么代码，所以您不会去编写任何代码。 </P>
<P><A name=2><SPAN class=atitle2>如何先编写测试</SPAN></A><BR>整个理论很棒，但如何 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">先</I>编写测试呢？首先，我推荐您阅读 Kent Beck 撰写的 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Test-Driven Development: By Example</I>（请参阅 <A href="EditPosts.aspx?opt=1#resources" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）一书，里面列举了一个详尽的贯穿于整本书的示例。该书不仅讲述了如何编写测试和让这些测试来驱动您的代码的原理，而且还讲述了测试驱动的编程为什么是一种好的编程方法。这里我将举一个简单的例子，让您体会一下我正在讲什么。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N10090><B>测试驱动 vs. 先测试</B></A><BR>我喜欢用&#8220;测试驱动&#8221;这个术语，而不喜欢用&#8220;先测试&#8221;这个术语，因为先测试强调了在编写代码前编写程序员测试这个原理。这些原理很重要，但真正的力量在于测试驱动所隐含的想法和编程习惯的改变。&#8220;测试驱动的编程&#8221;这一更贴切的术语包含两种测试，它指出 XP 团队强调让测试驱动他们所要做的一切这一方式。</P></TD></TR></TBODY></TABLE></P>
<P>假定我正在编写包含 <CODE>Person</CODE> 对象的系统。我希望在我问每个 <CODE>Person</CODE> 时，他／她能告诉我其年龄（作为整数）。即使我还没有编写一丁点代码，但也该编写测试了。&#8220;什么？&#8221;，您可能会说，&#8220;我甚至不知道在测试什么，怎么编写测试？&#8221;答案很简单，您 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">的确</I>知道您在测试什么，只是不 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">知道</I>您所了解的内容，因为您不习惯按这样的方式进行思考。这就是我的意思。 </P>
<P>您确实还没有任何代码，但您脑海中应有 <CODE>Person</CODE> 对象的雏形。 <CODE>Person</CODE> 对象上应该有一个方法，该方法可以用整数形式返回年龄。因为我最常使用 Java 语言，所以我用 JUnit 来编写程序员测试。清单 1 显示了我为 <CODE>Person</CODE> 对象编写的 JUnit 测试： </P><A name=N100BA><B>清单 1. 用于 Person 对象的 JUnit 测试</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.roywmiller.testexample;

import junit.framework.TestCase;

public class TC_Person extends TestCase {

	protected Person person;

	public TC_Person(String name) {
		super(name);
	}

	protected void setUp() throws Exception {
		person = new Person();
	}

	public void testGetAge() {
		int actual = person.getAge();
		assertEquals(0, actual);
	}

	protected void tearDown() throws Exception {
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>首先，让我向那些不熟悉 JUnit 的人讲述一些浅显的原理。 <CODE>TestCase</CODE> 类是您将最常使用的类。您只是写了一个测试类（在该示例是 <CODE>TC_Person</CODE> ），它是 <CODE>TestCase</CODE> 的子类。（注：在 JUnit 3.8.1 中，可以有也可以没有接受 <CODE>String</CODE> 的构造函数，但由于我几乎所有的 Java 开发都在 Eclipse IDE（请参阅 <A href="EditPosts.aspx?opt=1#resources" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）中完成，Eclipse IDE 免费向我提供了这个构造函数，所以我就把它保留在这里了。）一旦创建好测试类之后，测试方法中要有实际的动作。这些方法都恰如其分地用前缀 <CODE>test</CODE> 开头（它们必须是 <CODE>public</CODE> ，并且返回 <CODE>void</CODE> ）。当运行测试时，JUnit： </P>
<UL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>内省测试类，并执行每个以&#8220;test&#8221;开头的方法 
<LI>在执行每个测试方法之前执行 <CODE>setUp()</CODE> 方法 
<LI>在执行每个测试方法之后执行 <CODE>tearDown()</CODE> 方法 </LI></UL>
<P>在该示例中， <CODE>setUp()</CODE> 方法中没有太多要执行的语句。它只是实例化 <CODE>Person</CODE> （我用这个方法是让您觉得这个测试案例看上去很&#8220;完整&#8221;）。这意味着，如果这里有 20 个测试方法，则每个测试方法都以一个新的 <CODE>Person</CODE> 实例开始。 <CODE>tearDown()</CODE> 中不做任何事情，所以现在它是空的。值得强调的一点，您不需要 <CODE>setUp()</CODE> 或 <CODE>tearDown()</CODE> ；我通常直到编写第二个或第三个测试方法，并确定了这些方法都共享某些公共的设置或销毁活动时，才创建它们。 </P>
<P>有了这些原理之后，要注意，我在测试方法中制订了一些设计决策。我假定，可以构造一个 person，并且&#8220;缺省&#8221; <CODE>Person</CODE> 会返回值为 0 的 age。还假定 <CODE>Person</CODE> 对象有 <CODE>getAge()</CODE> 方法。即使那些假定不会一直都成立，但目前它们还适用。可以说，这是一个简单的测试，让我说明测试驱动的编程。有了这些假定之后，实例化 <CODE>Person</CODE> （在 <CODE>setUp()</CODE> 中实例化 <CODE>Person</CODE> 只是为了展示如何使用 setUp() 方法），接着调用测试方法中正在测试的方法，然后调用其中一种&#8220;断言（assert）&#8221;方法。断言方法测试事情是否为 true。换句话说，这些方法针对某件事做出一个断言，该断言告诉 JUnit 验证该事是否为 true。表 1 列出了断言的类别： </P>
<P><A name=table1><SPAN class=atitle3>表 1. 断言类别</SPAN></A><BR>
<TABLE width=600 border=1>
<TBODY>
<TR>
<TD><B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">断言方法</B> </TD>
<TD><B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">描述</B> </TD></TR>
<TR>
<TD><CODE>assertEquals</CODE> </TD>
<TD>比较两件事物是否相等（基本类型或对象）</TD></TR>
<TR>
<TD><CODE>assertTrue</CODE> </TD>
<TD>对布尔值求值，看它是否为 true</TD></TR>
<TR>
<TD><CODE>assertFalse</CODE> </TD>
<TD>对布尔值求值，看它是否为 false</TD></TR>
<TR>
<TD><CODE>assertNull</CODE> </TD>
<TD>检查对象是否为 null</TD></TR>
<TR>
<TD><CODE>assertNotNull</CODE> </TD>
<TD>检查对象是否不为 null</TD></TR>
<TR>
<TD><CODE>assertSame</CODE> </TD>
<TD>检查两个对象是否为同一实例</TD></TR>
<TR>
<TD><CODE>assertNotSame</CODE> </TD>
<TD>检查两个对象是否不为同一实例</TD></TR></TBODY></TABLE></P>
<P>在这里，我检查 <CODE>Person</CODE> 实例的 age 是否为 0，新 <CODE>Person</CODE> 对象的缺省值为 0。 </P>
<P>当然，这个测试甚至不能编译。&nbsp;</P>
<P>显然，我还没有 <CODE>Person</CODE> 类，所以运行该测试会出现问题 — JUnit 给出了一个红条。如果可以运行，并通过测试，则会显示一个绿条。您的目标总是设法得到一个绿条。别忘了，JUnit 的座佑铭是&#8220;得到绿条，使代码干净&#8221;（有时抱怨是难免的）。 </P>
<P>没问题。我将创建 <CODE>Person</CODE> 类，如清单 2 所示： </P><A name=N101D4><B>清单 2. Person 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.roywmiller.testexample;

public class Person {
	
	public int getAge() {
		return 0;
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>现在，当运行这个测试时，测试通过，应该可以看到一个绿条。我必须从 <CODE>getAge()</CODE> 返回值，否则不会编译它。这里碰巧 0 最方便，0 被认为是新的 <CODE>Person</CODE> 实例的缺省值，所以工作正常。再次重申，我只编写了通过测试所需的代码。 </P>
<P>能够使 <CODE>Person</CODE> 具有缺省的年龄值固然很好，但这对我的系统不会有太大帮助。 <CODE>Person</CODE> 需要比这更智能些。我真正所需要的是， <CODE>Person</CODE> 拥有其生日，并能回答其当前的年龄。这意味着 <CODE>Person</CODE> 对象的年龄会随时间的推移而增长。在进行编码前，将 <CODE>testGetAge</CODE> 重命名为 <CODE>testGetDefaultAge</CODE> （清楚地表明，我正在测试缺省的年龄），并为这个测试案例编写另一个测试方法，如清单 3 所示： </P><A name=N10204><B>清单 3. 新的测试方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public void testGetAge() {
	GregorianCalendar calendar = new GregorianCalendar(1971, 3, 23);
	person.setBirthDate(calendar.getTime());
	int actual = person.getAge();
	assertEquals(31, actual);
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>还不能编译这个测试（您注意到了其中的模式吗？），因为 <CODE>Person</CODE> 内没有 <CODE>setBirthDate()</CODE> 方法。在创建了这个方法之后， <CODE>Person</CODE> 将类似于清单 4 所示： </P><A name=N1021D><B>清单 4. 更新的 Person 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.roywmiller.testexample;

import java.util.Date;

public class Person {
	
	protected Date birthdate;
	
	public int getAge() {
		return 0;
	}
	
	public void setBirthDate(Date aBirthDate) {
		this.birthdate = aBirthDate;
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>Person</CODE> 中的 <CODE>getAge()</CODE> 仍然没有什么变化，所以测试失败。<STRONG></STRONG><BR>&nbsp;</P>
<P>生成的 <CODE>AssertionFailedError</CODE> 告诉我结果不是 31 而是 0。这个失败在预料之中，因为我没有改变 getAge() 方法来做某些不同的事。现在仅仅编写足够使测试通过的代码（这里有两个测试）。我必须允许年龄的缺省值为 0，但我必须计算出生于 1971 年 3 月 23 日的人的年龄。一些程序员（包括 Kent Beck）建议在这一点上尽可能简单，譬如检查 <CODE>birthdate</CODE> ，看它是否为 null ― 如果为 null，则返回 0，否则返回 31 ― 然后编写另一个测试使计算更智能。一小步一小步地思考问题这种方法是很好的技术，我们要采用这种技术，当您想回到上面提到的基本规程来使自己摆脱调试惯例时，那是再好不过。但这里我想使该示例略微简单些，所以我仅仅试图通过按我所希望的方式，用 <CODE>Calendar</CODE> 计算年龄，使该测试通过。清单 5 显示了 <CODE>Person</CODE> 中我所编写的代码： </P><A name=N10254><B>清单 5. getAge() 实现</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.roywmiller.testexample;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class Person {
	
	protected Date birthdate;
	
	public int getAge() {
		if (birthdate == null)
			return 0;
		else {
			int yearToday = 
			  Calendar.getInstance().get(Calendar.YEAR);
			
			Calendar calendar = new GregorianCalendar();
			calendar.setTime(birthdate);
			int birthYear =calendar.get(Calendar.YEAR);
			
			return yearToday - birthYear; 
		}
	}
	
	public void setBirthDate(Date aBirthDate) {
		this.birthdate = aBirthDate;
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>当我运行测试时，我失败了，预期的结果为 31，但实际结果为 32。怎么了？唔，我知道问题一定出在刚才所写的代码中，没有进一步考虑下去。在检查完 <CODE>else</CODE> 子句之后，我明白我只是根据年来计算年龄。这不对。我现在 31 岁，但这个月再过几天我要 32 岁了（但我写该代码时，是 3 月份），我的算法造成错误的结果。所以需要重新考虑 <CODE>getAge()</CODE> 。我用清单 6 中的代码段纠正了这个错误： </P><A name=N10269><B>清单 6. 改正后的 getAge()</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
else {
	int yearToday = Calendar.getInstance().get(Calendar.YEAR);
	
	Calendar calendar = new GregorianCalendar();
	calendar.setTime(birthdate);
	int birthYear = calendar.get(Calendar.YEAR);
	
	if (yearToday == birthYear)
		return yearToday - birthYear;
	else
		return yearToday - birthYear - 1;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>绿条！在 <CODE>Person</CODE> 类中有一些重复的代码，但我把它留给稍后的重构练习。欢迎替我清理该代码。您可以有信心地做这件事，因为可以运行测试来证实您没有破坏任何事物。 </P>
<P>这个示例使您体会到了测试驱动的编程类似于什么。我只在每步编写足够让测试通过的代码。作为一种理论，这在思想倾向上是一种挑战。您必须习惯这种思想，在编写代码 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">之前</I>，可以并应该编写测试。在通过所有测试之后，就完成了工作。 </P>
<P>在先编写测试时，必须习惯故意只看眼前。清单 6 中的示例是一个十分简单的情形。即使最简单的编程问题，在实际当中通常要更复杂。这种方法有助于将问题分解成更可管理的部分，但您最终仍可能遇到一些复杂的令人头疼的问题。在那些情况下，必须使自己不要考虑太远，不要假定它的&#8220;普适性&#8221;有多高，也不要假定这种方法能处理某些尚未遇到的情形。仅仅编写测试，使它通过。您需要采取一些较小的步骤，然后编写迫使您要采取更多步骤的测试。请记住，您正在测试代码的存在性，如果您以较小的步骤来编写代码，那您就做对了。 </P>
<P><A name=3><SPAN class=atitle2>为什么应该先编写测试&#8230;&#8230;</SPAN></A><BR>也许您不认为先编写测试是一个好主意。它看上去似乎很奇怪，或者也许似乎没有必要。我常从富有经验的程序员那听到第二种原因。这些程序员很聪明，他们具有许多经验，他们说不需要先编写测试，因为他们知道自己在做什么。我能领会，但我怀疑他们存在一个隐式的假定：他们没能领会先编写测试。恕我难以苟同。事实上，我认为采用先编写测试方法有三个原因： </P>
<UL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>学习 
<LI>新问题 
<LI>信心 </LI></UL>
<P>先编写测试 ― 到后面再执行这些测试 ― 是较佳的学习方式。它使您能将精力集中在所编写代码的接口部分。在编写测试时，您假设正在使用的类已经存在，然后按照您希望在系统其余部分中使用的方式来使用该类。稍后，当您忘记如何使用该类时，可以查看测试，看一个非常具体的示例。这是学习的很好方式。 </P>
<P>关于先编写测试最有趣的事情之一是，它有助于发现新问题。您正在使创建之中的系统&#8220;成长&#8221;起来。如果您正在使用 XP，则没有预先设计整个事情 ― 而是一边开发一边设计。在您先编写测试并通过测试这个过程中，您正在让代码告诉您它想要做什么，以及会成为什么。如果仅仅着手编码工作，则您完全按照您的设想来行事了。越晚做决定，则越有可能发现新问题和新动向，这些可使您的系统更完善。 </P>
<P>但是，我所喜欢的先编写测试的好处是让这些测试在稍后执行。在我先编写测试时，我有许多奇异的逻辑。我可能没有涵盖代码的方方面面，但会包括其中许多方面。在任何情况下，我会有一套测试，这套测试比我曾参与过的大多数没采用 XP 的项目要更好。我可以按一个按钮就运行这些测试。几秒种之后，我就知道代码是否按我告诉它应该怎样的方式来运行。这种可回归的工具是很有价值的。我团队中的任何人（或任何地方的任何人）可以在任何时候更改代码，甚至在代码发布的前一天也可以更改，因为如果有任何问题，测试会立即告诉他们。作为一名程序员，这给予了我信心 ― 比大多数程序员具有更大的信心。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=N102A3><B>帮助形成&#8220;揭开极端编程的神秘面纱&#8221;的未来</B></A><BR>一如既往，我热忱邀请您就以后的专栏文章提出您的反馈意见，这样有助于促进这个专栏。关于 XP，您存在的最大问题是什么？您认为是完全愚蠢的、不明智的、非专业的还是不可能的？最让您感到迷惑的做法是什么？请在本文的 <A href="javascript:void forumWindow()" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">论坛</A>提出您的建议，或者就直接 <A href="mailto:rmiller@rolemodelsoftware.com" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">给我发电子邮件</A>。 </P></TD></TR></TBODY></TABLE></P>
<P><A name=4><SPAN class=atitle2>&#8230;&#8230;为什么人们没有采用</SPAN></A><BR>许多没有先编写测试的程序员甚至不知道还可以使用这种方法。如果他们知道，也可能对如何使用它感到迷惑，或者他们可能想知道为什么要这样做。即使他们知道如何去做，并认为它是一种好的想法，但许多人仍然没有先编写测试。 </P>
<P>先编写测试需要遵循一定的规程。作为一名程序员，我认为，对于我正在开发的工作，不编写测试可能会更容易些。有时确实如此，但通常只会在短期内是这样。如果我经常不写测试，那么不久会有一堆代码没有经过测试。当编写下一个系统功能部件时，可能会出现不正常现象，问题出在哪里？没有测试，我无法胸有成竹地回答这个问题。即使一切似乎都工作良好，但我不能确保过去在系统中没有出现的问题在以后还不会出现。这种恶性循环就是为什么大多数程序员讨厌测试人员告诉他们代码出现问题的原因。在没有测试的前提下，跟踪错误造成了加班加点以及对工作的不满意。 </P>
<P>在我用那种方式向大多数程序员说明这种情况时，他们认为测试驱动的编程是一个不错的想法 ― 这之后，他们仍然不使用这种方法。在编写代码前编写测试这种作法意味着，在测试运行并失败之前，不会做工作中真正有趣的部分。不要掉入这个陷阱，否则您以后会付出很多。 </P>
<P><A name=5><SPAN class=atitle2>难以处理的情形</SPAN></A><BR>在人们开始编写测试时，总是会遇到这样一些情形：他们说，&#8220;只是没有办法进行测试&#8221;。XP 社区的一些人可能毫不含糊地说，不写测试就永远别写代码。您应该努力尝试这么做，但以我个人的经验，有时我发现有些地方我也不能这么做。如果您发现自己处在这种情形，您应该放弃吗？在一定程度上可以。我认为您可以做两件事： </P>
<UL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>在编写测试前编写代码 
<LI>在很少情况下，根本不编写测试，放到后面编写 </LI></UL>
<P>如果发现在尝试先编写测试之后，仍不能先编写测试，那么回到测试中来。我仍然希望进行测试，这样我可以从完整的回归套件中获得信心，但我必须先编写一些代码，然后编写测试。有时我编写了一点代码，然后编写一点测试，这样两者可以一起并进。在少数情况下，我恰好根本想不出如何编写测试。在出现这种情形并且我的结对搭档也想不出法子时，我就问问其他人（例如，另一对搭档），看看他们是否什么聪明点的主意。有时这很管用。但还有一些时候，整个团队都陷入了困境。在那些情况下，必须选择可行性。我可能暂停编码，陷入困境，或者在没有测试的情形下，编写一些代码，到稍后再编写测试。也许代码中出现的第一个错误会使测试什么以及如何测试变得更为明晰。这些是可行的规则。 </P>
<P><A name=6><SPAN class=atitle2>测试工具和技术</SPAN></A><BR>在这世上，几乎每种语言都有一个 xUnit 库。对于 Java 平台，则由 JUnit 担当此任。我个人使用 Eclipse IDE（请参阅 <A href="EditPosts.aspx?opt=1#resources" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>），它极好地集成了 JUnit。Eclipse 是开放源码，有它自己的测试套件，您可以使用它。使用这个合适工具，您可以编写大量好的测试。但有时最好有一些其它帮助。幸运的是，可以利用一些编码技术来更方便地进行测试，甚至可以测试 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">看上去</I>不可测试的事物。可以使用的一些技术包括 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">ObjectMother</I>模式、 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">模仿对象（Mock Object）</I>和 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">伪（Sham）</I>对象。 </P>
<P><A name=N102F2><SPAN class=atitle3>ObjectMother 模式</SPAN></A><BR>ObjectMother 模式实际是 Gang of Four Abstract Factory 模式（请参阅 <A href="EditPosts.aspx?opt=1#resources" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）的实现，它告诉您创建一个工厂对象来给出需要测试对象的实例。例如，假定您正在构建一个处理客户预订讲座的系统。您可能构建一个 ObjectMother 对象，使 <CODE>Seminar</CODE> 对象具有不同种特征，您可以用这些特征来测试某些情况。在 Java 语言中，您可能创建 <CODE>TF_Seminar</CODE> 对象，它有几个静态工厂方法 ― 也许称为 <CODE>createSomething</CODE> 或 <CODE>newSomething</CODE> 。用您对正在创建事物的一些描述代替&#8220;something&#8221;，譬如 <CODE>newFullyLoaded</CODE> ，用它来创建具有所有数据成员、并且这些数据成员都已填有已知数据的 <CODE>Seminar</CODE> 。这样做使得测试数据放在一个地方，从而使代码更干净，更容易重构。在代码中，每当需要完全装入的 <CODE>Seminar</CODE> 来进行测试时，可以象清单 7 那样做： </P><A name=N1031B><B>清单 7. ObjectMother 示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Seminar seminar = TF_Seminar.newFullyLoaded;
seminar.doSomething();
assertEquals("expectedValue", seminar.getValue());
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N10325><SPAN class=atitle3>模仿对象</SPAN></A><BR>模仿对象使您可以为测试而模仿对象（请参阅 <A href="EditPosts.aspx?opt=1#resources" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。给模仿对象一个接口，您希望实际组件具备这个接口，然后使用模仿对象，直到实际组件成形。但模仿对象不仅仅只是还未存在的组件的存根。可以评估代码如何与模仿对象交互（譬如，验证调用了某个方法多少次以及检查状态等）。最近模仿对象得到了大力推广，但我认为它们被滥用了，它们太&#8220;重&#8221;以至于不切实际。 </P>
<P><A name=N10332><SPAN class=atitle3>伪对象</SPAN></A><BR>有时我所希望的是一个伪对象，它实现了与真实对象相同的接口，可以回答关于我在测试中如何与它交互这样一些特定问题。这就是伪对象 ― 一种用来伪装测试中对象的轻量型方式。伪对象可以是您所需要的任何对象。它是我曾使用过的最全面灵活的工具、模式和思考方式，我推荐您使用它。例如，我在前面创建的 <CODE>Person</CODE> 对象的伪对象类似于清单 8 中的样子： </P><A name=N1033F><B>清单 8. Person 的伪对象</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
protected class ShamPerson extends Person {
	protected boolean getAgeWasCalled;
	
	public int getAge() {
		getAgeWasCalled = true;
		return 25;
	}
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>如果可行，我总是试图采用正在测试类的伪类（在这里是 <CODE>ShamPerson</CODE> ）作为测试中的内部类。通过这样做，从而向不必需要伪类的其它事物隐藏了该伪类。 </P>
<P>一旦有了伪对象，我就可以在不直接测试 <CODE>Person</CODE> ，而是测试其它代码如何与 <CODE>Person</CODE> 实例进行交互的测试中用到它。我可以实例化 <CODE>ShamPerson</CODE> ，然后与它交互，然后断言 <CODE>getAgeWasCalled</CODE> 为 true。 </P>
<P><A name=7><SPAN class=atitle2>编程革命</SPAN></A><BR>在编写代码前编写测试极大地改变了我作为程序员的生活，它同样也可以改变您的生活。我的代码始终比先编写测试之前所写的代码更简单、更干净以及更健壮。只要记住这条规程 ― 在编写代码之前考虑如何测试代码 ― 就可以使代码变得更好。如果每个软件开发团队不采用其它 XP 做法，并且只是先编写测试，则软件开发世界将会令人惊异地变得更好。采用这一点做法，任何程序员都可以先编写测试。这些工具（JUnit 和 Eclipse 等）是免费的，只等您去实践它。我已经看到投资得到了及时的回报，我相信您也会这样做的。 </P><img src ="http://www.cnblogs.com/maxpain/aggbug/174750.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47996/" target="_blank">7月编程语言排行榜</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>揭开极端编程的神秘面纱: 工作的首选（XP)工具</title><link>http://www.cnblogs.com/maxpain/archive/2005/06/15/174746.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Wed, 15 Jun 2005 03:04:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2005/06/15/174746.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/174746.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2005/06/15/174746.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/174746.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/174746.html</trackback:ping><description><![CDATA[<P>做java很多年了，其间也遇到不少人问我为什么java开发成本高，而且效率低。呵呵，我只能告诉他们是你们的开发模式和组织形式上有问题。这里有一些文章，其中对极限编程做了较好的表述。作为一个入门的了解还是很有帮助的。<BR><BR><SPAN class=atitle2>选择最好的工具来帮助团队适当实施XP</SPAN><BR></P>
<P><A href="EditPosts.aspx?opt=1#author1"><NAME>Roy W. Miller</NAME></A><BR>Software Developer, RoleModel Software, Inc.<BR>2003 年 7 月 31 日</P>
<BLOCKQUOTE><ABSTRACT-EXTENDED>希望尝试XP的团队经常不知道从何开始。通常他们有太多关于XP的问题。但技术之后将是什么呢？这个月，Roy Miller结合理论和实践来讨论您应使用什么工具以及如何使用它们。请在附带的 <A href="javascript:void forumWindow()" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">讨论论坛</A>中与作者和其他读者一起分享您有关本文的心得体会（您也可以点击文章顶部或底部的 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">讨论</B>来访问论坛）。 </ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>当谈到您实施XP需要的工具时，坏消息是您通常需要学习一些新知识。好消息是您首先只需要少数几件工具。您无需大量文件创建工具和设计工具来形成一个大软件。最后您只需要一些非技术性设备，少数软件和一些专职团队成员。记住：XP就是做最简单的事，这可以应用于设计、代码、流程 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">和</I>工具。 </P>
<P>我已经编写软件和管理项目十年了。在那段时期，我使用大量优秀的工具来管理和编程。在这样做之后，我可以心安理得的说人们太依赖于工具了。工具有自己的一席之地，但不能替代技能和经验。</P>
<P>我建议您和您的团队以相对简单的工具开始（可能是继续使用），它们可以提供大量的功能，为您和您的团队免去了繁重的学习之苦。假设您一直在使用Java语言(实际上，这就是Java技术专区)，在您的项目中可以考虑使用以下工具：</P>
<UL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><B>Eclipse</B>，作为您主要的团队友好的开发环境 
<LI>您最喜欢的 <B>源代码控制工具</B>(我使用CVS的时间与使用Eclipse的时间一样长) 
<LI><B>JUnit</B>用于编程人员测试并可以作为客户测试的一个引擎 
<LI><B>HttpUnit</B>用于测试Web应用程序 
<LI><B>Ant</B>用于您 <I>每天</I>开发自己的应用程序 
<LI><B>Shams</B>用于在需要它们存在的测试中，剔除您没有构建的组件 </LI></UL>
<P>我们将把本文的重点放在有效使用Eclipse、JUnit和Ant上。(HttpUnit是下一篇文章的主题，我们在前一篇文章&#8220; <A href="http://www.cnblogs.com/developerworks/cn/java/j-xp042203/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">测试驱动的编程</A>&#8221;中介绍了Shams。) </P>
<P><A name=1><SPAN class=atitle2>从Eclipse开始</SPAN></A><BR>在我的编程人员职业生涯中，我使用了多种IDE。当我第一次开始使用VisualAge for Java (VAJ)时，需要学习的知识都快把我淹没了，但在两三个星期之后，我奇怪为什么其它人 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不使用</I>VAJ也可以编写Java软件。这是我使用过的最令人惊讶的IDE。而且，Eclipse性能更优越 -- 并且它是免费的(真的)。 </P>
<P>Eclipse基于一个简单的假设：团队应有一个真正集成的开发环境。整个应用程序都使用Java语言来编写，以便使用Java语言来进行开发。如果您希望看到功能丰富、well-factored的代码，那么看看Eclipse的源代码吧！该应用程序真正是构建应用程序的一个框架，基于插入式架构。几乎Eclipse中的所有组件都是插入式的 -- 甚至Java语言开发支持，它有一个任何组件都可插入的核心平台。在Eclipse站点上和 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks</I>(见 <A href="EditPosts.aspx?opt=1#resources" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>)提供了大量关于如何开始的文章，因此我将简要介绍如何使用这一工具。 </P>
<P>安装很简单。登录 <A href="http://eclipse.org/downloads/index.php" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Eclipse下载页面</A>，选择一个镜像站点然后下载最新的stable build或最新版本(2.1)。不要认为您必须等待新版本来升级Eclipse --Eclipse团队的目标很明确，测试第一，他们提供整套测试，因此您可以核查以确保stable build是稳定的。 </P>
<P>在下载了该版本之后，完成以下步骤来安装和运行这一版本(假设您使用Windows操作系统)：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>安装JDK 1.4(如果您没有JDK1.4，关于其下载的相关信息请参阅 <A href="EditPosts.aspx?opt=1#resources">参考资料</A>)。 
<LI>将Eclipse压缩文件解压缩到C:\eclipse。 
<LI>在您的硬盘上建立单独的目录，叫做 <CODE>C:\EclipseWorkspaces</CODE> 。 
<LI>在开始菜单上建立 <CODE>C:\eclipse\eclipse.exe -data C:\EclipseWorkspaces\&lt;your workspace name&gt;</CODE> 的快捷方式，从 <CODE>C:\eclipse</CODE> 中开始。 
<LI>双击新快捷方式。Eclipse将在 <CODE>C:\EclipseWorkspaces</CODE> 中建立workspace的子目录。 </LI></OL>
<P>第一次运行Eclipse时，您将看到与图1类似的界面：</P>
<P><A name=IDA0WHRE><B>图1：Eclipse欢迎界面&nbsp;</B></A><BR> <IMG height=450 hspace=5 src="http://www.cnblogs.com/images/cnblogs_com/maxpain/eclipseWelcome.gif" width=600 align=baseline></P>
<P>欢迎界面是Eclipse Workspace <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">资源视图</I>的一部分。这一视图使您能够查看所有文件。该表在导航视图的左上角显示。Eclipse完全可定制，您可以将视图移到您希望的任意位置。目前我们将它们单独放置并转向Java Browsing视图。通过选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Window &gt;Open Perspective</B>，您可以发现那个视图可用。选择Java Browsing视图，如图2所示： </P>
<P><A name=IDAMXHRE><B>图2：Java Browsing视图</B></A><BR><IMG height=450 hspace=5 src="http://www.cnblogs.com/images/cnblogs_com/maxpain/javaBrowsing.gif" width=600 align=baseline>&nbsp;</P>
<P>Java Browsing视图在顶部有四个视图，在底部有一个编辑器。四个视图从左到右显示项目、包、类型和成员。包、类型和成员是您应该很熟悉的Java语言结构。项目是Java包的Eclipse Meta容器。您可以在这一视图上进行大多数开发工作，而资源视图更便于处理文件而不是源代码和类文件。</P>
<P>要开始开发，完成以下步骤以创建称为 <CODE>Sample</CODE> 的Java项目： </P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>点击 <B>Window &gt;Preferences &gt;Java &gt;New Project</B>。 
<LI>点击 <B>Folder</B>单选按钮，然后点击 <B>OK</B>。 
<LI>点击 <B>File &gt;New &gt;Project</B>. 
<LI>选择 <B>Java Project</B>，然后点击 <B>Next</B>。 
<LI>输入 <B>Sample</B>作为项目名称，然后点击 <B>Finish</B>。 </LI></OL>
<P>现在您应该在项目视图中看到 <CODE>Sample</CODE> 项目。此时，点击工具栏上的 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Create a Java Package</B>按钮，创建称为 <CODE>com.sample</CODE> 的包。选择这一包。现在您已经准备创建您的第一个Java类，了解Eclipse如何帮助继续进行XP开发。 </P>
<P><A name=2><SPAN class=atitle2>JUnit</SPAN></A><BR>XP是过去十年内我的软件开发职业生涯中单个最大的影响。作为一位编程人员，受最大影响的实践（Practice)(XP或其它方面)是在我编写代码 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">之前</I>编写测试程序。正如我 <A href="http://www.cnblogs.com/developerworks/cn/java/j-xp042203/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上月</A>所述，编写足够的代码以使测试通过可以简化程序代码，从而有更多的精力关注手边的工作。上个月我讨论了一些关于使用JUnit的技巧，在此处我将进行详细阐述。 </P>
<P>JUnit与Eclipse一同提供，因此您无需进行下载。如果您不使用Eclipse，您可以下载JUnit (见 <A href="EditPosts.aspx?opt=1#resources" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>)。要使用JUnit，您必须首先将JUnit JAR保存在项目的Build路径上并创建一个测试类。完成以下步骤以将JUnit保存在项目的Build路径上： </P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>右击 <CODE>Sample</CODE> 项目，选择菜单底部的 <B>Properties</B> 
<LI>选择 <B>Java Build Path</B> 
<LI>选择 <B>Libraries</B>标记 
<LI>点击 <B>Add Variable</B>按钮 
<LI>点击 <B>New</B>按钮，输入 <B>JUNIT_LIB</B>作为变量名称 
<LI>编辑该变量并指向 <CODE>C:\eclipse\plugins\org.junit_3.8.1</CODE> 中的一个文件(JUnit是Eclipse插件) 
<LI>再次选择您Sample项目中的 <B>src</B>文件夹 </LI></OL>
<P>现在您把JUnit保存到了您的Build路径上。您可以直接向该路径添加外部JAR，但使用变量可以更简单的设置其它机器上的Workspaces(该变量是可以指向面向机器的位置的Meta名)。下一步是创建测试类：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>点击工具栏上 <B>Create a Java Class</B>按钮右侧的下拉箭头，选择 <B>Test Case</B> 
<LI>输入 <B>TC_Account</B>作为测试名 
<LI>选择 <B>setUp()</B>和 <B>tearDown()</B>复选框 
<LI>点击 <B>Finish</B> </LI></OL>
<P>现在您应该看到与图3类似的一个界面：</P>
<P><A name=IDAIZHRE><B>图3：打开测试案例</B></A><BR>&nbsp;<IMG height=450 hspace=5 src="http://www.cnblogs.com/images/cnblogs_com/maxpain/openTestCase.gif" width=600 align=baseline></P>
<P>您现在在类型视图中显示了 <CODE>TC_Account</CODE> 类，您可以在成员视图中看到该类的方法。您还在 <CODE>TC_Account</CODE> 类上打开了一个编辑器，编辑器中显示一些生成的代码和注释。我喜欢设置我自己的偏好来阻止显示所有生成的注释，您可以通过选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Window &gt;Preferences &gt;Java &gt;Code Generation</B>来实现。 </P>
<P><CODE>Account</CODE> 类将做什么？我们从能够向账户添加存款开始。这可能需要一种与下面类似的方法： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public void deposit(int amount)
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>向 <CODE>TestCase</CODE> 添加一种方法将测试 <CODE>Account</CODE> 的 <CODE>deposit</CODE> 方法。测试类现在看起来应像表1一样： </P>
<P><A name=IDAU0HRE><B>表1：JUnit测试</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sample;

import junit.framework.TestCase;

public class TC_Account extends TestCase {

	public TC_Account(String arg0) {
		super(arg0);
	}

	protected void setUp() throws Exception {
		super.setUp();
	}
	
	public void testDeposit() {
		Account account = new Account();
		assertEquals("Account should start with no 
		  funds.", 0, account.balance());
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>使用英语，您正在核查 <CODE>account.balance()</CODE> 返回0。注意测试甚至不进行编译，因为 <CODE>Account</CODE> 类并不存在。图4显示了当测试不编译时Workspace的显示界面： </P>
<P><A name=IDAI1HRE><B>图4：不进行编译的测试案例</B></A><BR>&nbsp;<IMG height=480 hspace=5 src="http://www.cnblogs.com/images/cnblogs_com/maxpain/testDoesntCompile.gif" width=506 align=baseline></P>
<P>界面底部的任务视图显示编译错误。点击任何错误将把您带到代码中真正出错的位置，它非常方便。实际上，Eclipse提供了一些象这样的方便之处。例如，注意有多种方法来表示您有编译错误。任务视图显示它，编辑器在左侧标记红色的X圆圈，workspace顶部的所有视图显示一个红色的X。如果您把鼠标盘旋在编辑器左侧的红色X上(在行附近显示错误)，hover text向您提供错误信息。</P>
<P>您可以随意盘旋在任何物体上，点击物体，进行另外一次实验以查找&#8220;隐藏&#8221;属性。它们遍布各处。但回到手头上的工作 -- 测试没有编译。因此，编写足够的代码来使测试进行编译、运行但结果失败。值得注意的是，我们正在努力采取一些基本措施并使事情尽可能简单并尽可能长的运行。通过选择 <CODE>com.sample</CODE> 项目来创建 <CODE>Account</CODE> 类，点击工具栏上的 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Create Java Class</B>按钮，输入 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Account</B>作为类名，然后点击 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Finish</B>。您现在应在 <CODE>Account</CODE> 类上打开了一个编辑器，它应该无任何方法。添加 <CODE>balance()</CODE> 方法。该类现在看起来应与表2类似： </P>
<P><A name=IDAM2HRE><B>表2：Account类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sample;

public class Account {
	public int balance() {
		return 0;
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>要进行测试，选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TC_Account</B>类，点击工具栏上&#8220;测试人员（running man）&#8221; 图标附近的下拉箭头，选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Run As &gt;JUnit Test</B>。测试将进行并将显示作为界面底部的一个视图。我喜欢使JUnit成为Fast视图，将其拖到workspace左侧，直到Eclipse让我把JUnit视图拖到Fast视图栏为止。此时，选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Window &gt;Preferences &gt;Java &gt;JUnit</B>，选中 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Show the JUnit results view only when a failure or error occurs</B>复选框。这将隐藏JUnit Fast视图，除非发生错误或失败。如果一切都正常运行，它将在图标的左下角显示一个绿色标记。. </P>
<P>编写足够的代码以使测试进行编译此时将为您提供即将通过的测试程序，但不是长期。现在向测试案例添加另一种断言，以在某些人调用 <CODE>deposit()</CODE> 方法后测试新的余额。 <CODE>testDeposit()</CODE> 方法现在看起来应与表3类似： </P>
<P><A name=IDAI3HRE><B>表3：JUnit的deposit()方法测试</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public void testDeposit() {
	Account account = new Account();
	assertEquals("Account should start with 
	  no funds.", 0, account.balance());

	account.deposit(5);
	assertEquals("Account should reflect 
	  deposit.", 5, account.balance());
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>编写足够的代码以使测试进行编译。在这种情况下，这意味着向 <CODE>Account</CODE> 增加do-nothing <CODE>deposit()</CODE> 方法。该类现在看起来应与表4类似： </P>
<P><A name=IDA13HRE><B>表4：使用空deposit()方法的Account</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sample;

public class Account {
	protected int balance;
	
	public int balance() {
		return balance;
	}
	
	public void deposit(int amount) {
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>再次点击测试人员图标来重新进行测试。JUnit Fast View显示，您将看到显示&#8220;Account should reflect deposit&#8221;信息的故障。现在您有一个即将失败的测试程序，它将告诉您真正需要编写那些代码。编写足够的代码来确保测试通过。使用 <CODE>deposit()</CODE> 方法将存款额添加到 <CODE>balance</CODE> 应毫于问题。当您重新进行测试时它们应该通过。 </P>
<P>这种&#8220;编写一个测试，编写足够的代码来使测试通过，重新进行测试&#8221;方法是您每天都在经历的XP开发流程。由于JUnit集成到Eclipse中，您需要的关于编程的一切都可以满足。运行测试很简单，就象呼吸一样。创建它们也是非常简单，因为通过生成代码，Eclipse为您保存了大量常见的输入。您只需要进行思考和考虑重要的事情。您的 <CODE>Account</CODE> 类现在看起来应与表5类似： </P>
<P><A name=IDAT4HRE><B>表5：实施了deposit()方法的Account</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sample;

public class Account {
	protected int balance;
	
	public int balance() {
		return balance;
	}
	
	public void deposit(int amount) {
		balance += amount;
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>能够存款是好事，但人们可能想要把它取出来。为 <CODE>Account</CODE> 的 <CODE>withdraw()</CODE> 方法编写一个测试。您的测试现在看起来应与表6类似： </P>
<P><A name=IDAG5HRE><B>表6：更新的Account测试</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sample;

import junit.framework.TestCase;

public class TC_Account extends TestCase {

	public TC_Account(String arg0) {
		super(arg0);
	}

	protected void setUp() throws Exception {
		super.setUp();
	}
	
	public void testDeposit() {
		Account account = new Account();
		assertEquals("Account should start with 
		  no funds.", 0, account.balance());
	
		account.deposit(5);
		assertEquals("Account should reflect 
		  deposit.", 5, account.balance());
	}

	public void testWithdraw() {
		Account account = new Account();
		account.balance = 5;
		account.withdraw(3);
		assertEquals("Account should reflect 
		  withdrawal.", 2, account.balance());		
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>通过向 <CODE>Account</CODE> 增加do-nothing <CODE>withdraw()</CODE> 方法来使测试进行编译，然后重新运行测试。测试失败。现在实施 <CODE>withdraw</CODE> 方法。 <CODE>Account</CODE> 类看起来应与表7类似： </P>
<P><A name=IDAG5HRE xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></A><A name=IDAEIIRE><B>表7：实施了withdraw()方法的Account</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sample;

public class Account {
	protected int balance;
	
	public int balance() {
		return balance;
	}
	
	public void deposit(int amount) {
		balance += amount;
	}
	
	public void withdraw(int amount) {
		balance -= amount;
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>所有测试都应通过。现在是时候进行集成了。您将回忆起在XP中&#8220;连续集成&#8221;是一项重要的实践。任何时候所有测试通过，您可以将代码集成到系统中。您应尽可能早并经常进行集成。Eclipse使这一切变得异乎寻常的简单。</P>
<P><A name=N102CD><SPAN class=atitle3>集成，集成，集成</SPAN></A><BR>为了准备您的Eclipse workspace进行集成，完成以下步骤；您将需要设置CVS：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>选择 <B>Window &gt;Open Perspective &gt;Other &gt;CVS Repository Exploring</B>，打开CVS视图。 
<LI>右击并选择 <B>New Repository Location</B>。输入与您的特殊设置有关的所有参数，然后点击 <B>Finish</B>。在CVS Repositories视图的表中应显示一个存储位置（Repository Location)。打开它您将看到一个HEAD流、一个Branches条目和一个Versions条目。HEAD条目是用户应集成的主要代码流(如果它们需要自己分叉并稍后与HEAD流合并，这主意不错)。 
<LI>关闭CVS视图窗口。 </LI></OL>
<P>要将代码集成到系统中，完成以下步骤：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>右击 <CODE>Sample</CODE> 项目。 
<LI>选择 <B>Team &gt;Share Project</B>。您正在&#8220;共享&#8221;一个以前未集成的项目。您通常只需这样做一次。 
<LI>根据提示，选择您希望的存储位置，然后点击 <B>Finish</B>. </LI></OL>
<P>现在XP流程应该是这样：&#8220;编写一个测试，编写足够的代码来使测试通过，重新运行测试，集成。&#8221;</P>
<P>现在我们来看一看 <CODE>TC_Account</CODE> 。注意是否有任何代码复制的现象？这两种测试方法都例示一个 <CODE>Account</CODE> 。JUnit框架在每个测试方法之前运行 <CODE>setUp()</CODE> 方法，因此这就是进行所有测试需要的任何设置的逻辑场所。例示 <CODE>Account</CODE> 对象合格。 </P>
<P>这是简单的refactoring，但Eclipse使其甚至更简单：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>进入 <CODE>TC_Account</CODE> 的编辑器 
<LI>右击 <CODE>testDeposit()</CODE> 中的 <CODE>account</CODE> 局部变量，然后选择 <B>Refactor &gt;Convert Local Variable to Field</B>. 
<LI>输入&#8220;account&#8221;作为字段名，选择 <CODE>protected</CODE> 访问限制符（access qualifier），然后点击 <B>OK</B>。现在您的测试类有一个叫做 <CODE>account</CODE> 的受保护的字段，测试方法对其进行了初始化。 
<LI>将初始化 <CODE>account</CODE> 的代码从测试方法移到 <CODE>setUp()</CODE> ，然后删除其它测试方法中的声明和初始化。您的测试类现在看起来应与表8类似： </LI></OL>
<P><A name=IDAHLIRE><B>表8：Refactored TC_Account</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sample;

import junit.framework.TestCase;

public class TC_Account extends TestCase {

	protected Account account;

	public TC_Account(String arg0) {
		super(arg0);
	}

	protected void setUp() throws Exception {
		super.setUp();
		account = new Account();
	}
	
	public void testDeposit() {
		assertEquals("Account should start with 
		  no funds.", 0, account.balance());
		
		account.deposit(5);
		assertEquals("Account should reflect 
		  deposit.", 5, account.balance());
	}
	
	public void testWithdraw() {
		account.balance = 5;
		account.withdraw(3);
		assertEquals("Account should reflect 
		  withdrawal.", 2, account.balance());		
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>您还得做一些工作，但Eclipse至少为您减少了一些单调乏味的输入工作。检查上下文菜单中其它refactorings变量。其中一些变量功能非常强大，可以节约您大量的时间：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>进入 <CODE>Account</CODE> 编辑器，然后点击 <CODE>balance()</CODE> 方法名。 
<LI>选择 <B>Refactor &gt;Rename</B>，然后输入&#8220;getBalance&#8221;作为新名称。 
<LI>确信选中&#8220;Update references to the renamed element&#8221;，然后点击 <B>OK</B>。 </LI></OL>
<P>这一简单的练习重命名了 <CODE>Account</CODE> 的accessor方法并更新了所有引用。重新运行测试以确保一切仍在正常运行。对于一个类来说，这可以很轻松地节约一分钟的时间，或者减少大量输入工作。想像一下，您有一个巨大的系统并有多个类调用 <CODE>getBalance</CODE> ，结果又将如何。您可以自由地在街上跳舞了！并且最重要的一件事是refactorings是可以撤销的。如果您输入了&#8220;getbalancew，&#8221;只需撤销就可以了。更有甚者，由于它如此简单，您只需再次refactor并更改这一名称。 </P>
<P>是时候进行再次集成了。记住流程了吗？我们更改了一些代码并重新进行了测试：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>右击 <CODE>Sample</CODE> 项目，然后选择 <B>Team &gt;Synchronize with Repository</B>来打开界面底部的Synchronize视图。双击蓝色标题栏来打开它。 
<LI>点击工具栏右侧的 <B>Incoming/Outgoing Mode</B>按钮。这一视图向您显示所有incoming和outgoing变化。换句话说，Eclipse联合CVS来向您显示workspace中内容与CVS内容之间的Delta。 
<LI>由于您以前没有提交任何事情，右击Structure Compare视图中的 <CODE>Sample</CODE> 项目，然后点击 <B>Commit</B>。 </LI></OL>
<P>想了解如果workspace中内容与CVS内容之间有Delta将会发生什么，返回 <CODE>Account</CODE> 类，右击 <CODE>deposit()</CODE> 方法的 <CODE>amount</CODE> 参数，然后选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Refactor &gt;Rename</B>，将名字更改为&#8220;anAmount。&#8221;瞧，结果发生了变化。重新运行测试。一切都应通过。现在右击该项目并再次同步。您应看到一个与图5类似的界面。如果您双击 <CODE>Account</CODE> 类，您将看到您刚创建的Delta。因为您想要保留它，右击该项目然后选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Commit</B>。 </P>
<P><A name=IDAUNIRE><B>图5：Synchronize视图</B></A><BR>&nbsp;<IMG height=480 hspace=5 src="http://www.cnblogs.com/images/cnblogs_com/maxpain/synchronizeView.gif" width=517 align=baseline></P>
<P><A name=3><SPAN class=atitle2>使用Ant来构建</SPAN></A><BR>在项目启动后，每个XP团队都应在每天结束时构建它们整个系统。从而团队能够向需要了解的任何人（例如，客户）提供正在运行的系统。它还为团队提供检查点（checkpoint）。如果系统某一部分发生严重故障，它们可以总是从昨天返回正在运行的系统。没有任何一项功能能象安全网一样使团队更具信心。</P>
<P>Jakarta的Ant项目是很好的构建工具。您使用XML来编写构建脚本程序。我发现XML&#8220;目标&#8221;有点神秘，当您对它越来越熟悉时这种神秘感也就慢慢裉去了。但是，我必须承认，如果没有使用Ant一段时间，我必须查找大量的信息来完成基本的工作。任何情况下，Eclipse都与一个Ant插件一同提供。集成是近乎无缝的。</P>
<P>现在我们为我们的项目－将文件复制到硬盘驱动器(在实现环境中，这可以是网络驱动器或一些其它驱动器)某处的部署目录－创建一个实例：</P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>选择workspace中的 <CODE>Sample</CODE> 项目 
<LI>选择 <B>File &gt;New &gt;Other</B> 
<LI>选择 <B>Simple</B>，然后选择 <B>File</B> 
<LI>命名新文件为build.xml。Eclipse创建该文件并在它上面打开一个编辑器(注意标题栏中的图标是Ant图标)。您的Build脚本程序看起来应与表9类似： </LI></OL>
<P><A name=IDA0OIRE><B>表9： Build脚本程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;project name="Sample" default="build" basedir="."&gt;
	&lt;property name="project" value="${basedir}"/&gt;
	&lt;property name="tempDirectory" value="${project}/temp"/&gt;	
	&lt;property name="runtimeClasses" 
	  location="${project}/lib/runtime.jar"/&gt;
	&lt;property name="deployDirectory" location="c:/deploy"/&gt;

	&lt;patternset id="non.test.classes" &gt;
			&lt;include name="**/*.class"/&gt;			
			&lt;exclude name="**/TC_*.class"/&gt;
	&lt;/patternset&gt;

	&lt;target name="build"&gt;
		&lt;antcall target="clean"/&gt;
		&lt;antcall target="init"/&gt;
		&lt;antcall target="build.Sample"/&gt;
		&lt;antcall target="clean"/&gt;
	&lt;/target&gt;
	
	&lt;target name="init"&gt;
		&lt;mkdir dir="${tempDirectory}"/&gt;
		&lt;mkdir dir="${deployDirectory}"/&gt;
	&lt;/target&gt;

	&lt;target name="build.Sample"&gt;
		&lt;javac srcdir="${project}/src"
			destdir="${tempDirectory}"
		&gt;
			&lt;exclude name="**/TC_*.java"/&gt;
		&lt;/javac&gt;

		&lt;jar destfile="${deployDirectory}/sample.jar"&gt;
			&lt;fileset dir="${tempDirectory}"&gt;&lt;/fileset&gt;
		&lt;/jar&gt;

	&lt;/target&gt;

	&lt;target name="clean"&gt;
		&lt;delete dir="${tempDirectory}"/&gt;
	&lt;/target&gt;	
&lt;/project&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>Ant基于 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">目标</I>，它描述Ant运行的工作单元。在这种情况下，您有三个目标。第一个是主目标，称为 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">build</I>。 <CODE>project</CODE> 标记的 <CODE>default</CODE> 属性确定build作为缺省目标。这一目标调用其它三个： <CODE>clean</CODE> 、 <CODE>init</CODE> 和 <CODE>build.Sample</CODE> 。如果它使用的是Java代码，您可以描述缺省目标为delegator方法 -- 它按正确的顺序调用其它目标(在某种意义上，它是Template Method模板的实施)。这种主要目标首先调用 <CODE>clean</CODE> 来确保系统开始刷新，然后调用 <CODE>init</CODE> 来设置必需的目录，接下来，它调用 <CODE>build.Sample</CODE> 然后再次调用 <CODE>clean</CODE> 。 <CODE>build.Sample</CODE> 目标是这项操作的所在地。使用 <CODE>build.Sample</CODE> ，您可以： </P>
<UL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>在本地硬盘驱动器上创建部署目录 
<LI>编译您所有的 <CODE>Sample</CODE> 项目源代码，不包括测试案例(无需部署它们)到临时目录 
<LI>JAR将编译后的类保存到临时目录并把它们保存到部署目录 </LI></UL>
<P>要运行Ant脚本程序，进入Resource视图，右击 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">build.xml</B>，选择 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Run Ant</B>，它将弹出一个选定了缺省目标的对话框。点击 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Run</B>。如果出错，Eclipse将在界面底部显示Ant Console。这是我对Ant最不满意的地方：如果发生错误，调试将是一种负担。幸运的是，Echo Ant目标为您提供等同于 <CODE>System.out.println()</CODE> 的功能。如果在您的脚本程序中发生错误，加入 <CODE>&lt;echo&gt;some helpful message&lt;/echo&gt;</CODE> 将帮助您了解发生了什么。在当前情况下，错误相当棘手。Ant抱怨它不能找到 <CODE>tools.jar</CODE> 。您需要告诉Ant什么地方能找到编译需要的Java类。要实现这一目标，按下步骤操作： </P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>选择 <CODE>Sample</CODE> 项目 
<LI>选择 <B>File &gt;Import</B>，然后选择 <B>File System</B>. 
<LI>浏览您安装的JDK中的 <CODE>lib</CODE> 目录 
<LI>选择 <CODE>tools.jar</CODE> 文件，然后点击 <B>Finish</B> </LI></OL>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=IDAL0IRE><B>帮助打造揭开极端编程神秘面纱的未来</B></A><BR>象往常一样，我邀请您提供对未来专栏的看法，一同帮助打造这一专栏的未来。您对 XP 最大的疑问是什么？您认为什么是十分愚蠢的、不明智的、不专业的或不可行的？什么是最让您困惑的方法？请在 <A href="javascript:void forumWindow()" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">讨论论坛</A>中提供您的建议或直接 <A href="mailto:rmiller@rolemodelsoftware.com" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">通过电子邮件与我联系</A>。 </P></TD></TR></TBODY></TABLE></P>
<P>完成这一系列步骤可以在 <CODE>Sample</CODE> 项目中创建一个 <CODE>lib</CODE> 目录并把 <CODE>tools.jar</CODE> 放入其中。现在告诉Ant如何找到它： </P>
<OL xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>选择 <B>Preferences &gt;Ant &gt;Runtime</B>。界面底部显示&#8220;Additional classpath entries:&#8221;。 
<LI>点击 <B>Add JARs...</B>按钮，然后选择 <B>Sample/lib/tools.jar</B>。现在Ant知道如何找到您的编译器。 
<LI>右击 <CODE>build.xml</CODE> 然后重新运行Ant。程序应无任何错误，您应该以将 <CODE>sample.jar</CODE> 保存在 <CODE>c:\deploy</CODE> 中结束。 </LI></OL>
<P><A name=4><SPAN class=atitle2>工具的优势</SPAN></A><BR>您将注意到在&#8220;must have&#8221;表中没有多少工具。您可能希望获得其它Eclipse插件，您还可能需要一些额外的Java语言库来支持您的特殊项目，但您不需要一大堆项目团队假设它们需要的工具。如果您正在使用XP，您唯一需要的管理工具是一些记录卡和一个电子表格。每样东西都是开发人员的工具。我认为您应该反对增加更多的插件，除非它们能使工作更加轻松。如果它们能做到，那就把它们加进来。好的工具能够使您享受工作。Eclipse、JUnit(以及其它测试助手，如shams和HttpUnit)和Ant都是我经常使用的工具。如果您需要一个应用程序服务器，使用Tomcat直到有人告诉您不允许这样做。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/library/j-xp052703/index.html" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">英文原文</A>. <BR><BR>
<LI>请参与本文的 <A href="javascript:void forumWindow()" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">论坛</A>。（您也可以通过单击文章顶部或底部的 <B xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">讨论</B>来访问该论坛。） <BR><BR>
<LI>看完本文之后您会加入本专栏吗？阅读第一篇 XP 文章，由 Roy Miller 和 Chris Collins 合作撰写的&#8220; <A href="http://www.cnblogs.com/developerworks/cn/java/j-xp/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">XP 精华</A>&#8221;（ <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks</I>，2001 年 3 月），从而搞清楚本专栏的出发点，然后一定要查看本专栏的 <A href="http://www.ibm.com/developerworks/library/j-xpcol.html" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">档案文章</A>。 <BR><BR>
<LI>阅读Richard Hightower和 Nicholas Lesiecki合著的 <A href="http://www.amazon.com/exec/obidos/ASIN/047120708X/qid%3D1051077441/sr%3D11-1/ref%3Dsr%5F11%5F1/103-6148347-9003855" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>Java Tools for Extreme Programming</I> </A>，了解支持XP的不同工具，包括JUnit和Ant。 <BR><BR>
<LI>Erik Hatcher 向您介绍了他如何修改流行的Ant 1.3 和JUnit测试框架，以提供完整、可定制的 <A href="http://www.cnblogs.com/developerworks/cn/java/j-junitmail/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">让编译和测试过程自动化</A>( <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks</I>，2001年8月)。 <BR><BR>
<LI>Eclipse最强大的一种特性是一组 <A href="http://www.ibm.com/developerworks/linux/library/l-eclipse.html" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">automated refactorings</A>，如本文中Daniel Steinberg所述( <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks</I>, 2001年11月)。 <BR><BR>
<LI>从 <A href="http://www.eclipse.org/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">eclipse.org</A>下载 Eclipse和阅读一些优秀的技术文章。 <BR><BR>
<LI><A href="http://www-124.ibm.com/developerworks/oss/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>developerWorks</I>Open source projects </A>提供大量关于Eclipse的信息。 <BR><BR>
<LI><A href="http://www.junit.org/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JUnit</A>是使用Java语言编写程序人员测试的首选工具。该站点链接大量了文章和工具。 <BR><BR>
<LI>有关为什么VisualAge for Java是XP小组的首选工具请阅读&#8220; <A href="http://www.ibm.com/developerworks/ibm/library/it-aprcc01/index.html" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Extreme Programming with IBM VisualAge for Java</A>&#8221;( <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks</I>，2001年4月)。 <BR><BR>
<LI>在 <A href="http://www.cnblogs.com/developerworks/cn/java/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>developerWorks</I>Java技术专区 </A>中您将找到数百篇关于Java编程的技术文章。 <BR></LI></UL>
<P></P>
<P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR>Roy W. Miller 从事软件开发和技术咨询工作将近十年了，最初在 Andersen Consulting（现在的 Accenture）工作，目前则在位于北卡罗莱纳州的 RoleModel Software, Inc. 工作。他使用过重量级方法和灵活方法，包括 XP。他与人合著了 Addison-Wesley XP 系列丛书中的一本（ <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Extreme Programming Applied: Playing to Win</I>），目前他正在撰写一本关于复杂性、紧急情况和软件开发的书（暂定标题为 <I xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Growing Software: Exploding the Myth of Prediction and Control</I>）。请通过 <A href="mailto:rmiller@rolemodelsoft.com" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">rmiller@rolemodelsoft.com.</A>或 <A href="mailto:roy@roywmiller.com" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">roy@roywmiller.com.</A>与 Roy 联系。您也可以通过 <A href="http://www.roywmiller.com/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">www.roywmiller.com.</A>访问他的个人网站。 </TD></TR></TBODY></TABLE></P><img src ="http://www.cnblogs.com/maxpain/aggbug/174746.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47996/" target="_blank">7月编程语言排行榜</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>hibernate3的中文查询问题</title><link>http://www.cnblogs.com/maxpain/archive/2005/06/10/171729.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Fri, 10 Jun 2005 03:31:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2005/06/10/171729.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/171729.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2005/06/10/171729.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/171729.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/171729.html</trackback:ping><description><![CDATA[hibernate当中使用hql进行查询的时候，如果参数是中文，这hql打印为乱码。以前从来没有遇到过的问题，郁闷。经过无数次的尝试之后都无法解决，查找资料突然发现这个hibernate3的一个bug。在hibernate3当中使用antlr来解析hql，估计是这个的问题。<BR>解决方法为：<BR>1、采用占位符，然后setString<BR>2、将 hibernate.query.factory_class 属性设置成 org.hibernate.hql.classic.ClassicQueryTranslatorFactory<img src ="http://www.cnblogs.com/maxpain/aggbug/171729.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47995/" target="_blank">Google Voice 上手</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[转]AppFuse中的SiteMesh问题</title><link>http://www.cnblogs.com/maxpain/archive/2005/06/05/168369.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Sun, 05 Jun 2005 09:37:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2005/06/05/168369.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/168369.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2005/06/05/168369.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/168369.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/168369.html</trackback:ping><description><![CDATA[http://www.blogbus.com/public/tb.php/1216085<BR>&nbsp;因为AppFuse中使用的默认的JSP模板为SiteMesh，默认情况下，所有的请求都会被SiteMesh所装饰，所以我要实现点击文件链接，直接下载文件时却得不到正确格式的文件，得到的是一个html文件，即使制定了下载文件的类型：如：application/msexcel或者其他的类型都不行，我分析是因为，我首先向输出流中写了些东西，后来被SiteMesh使用getWriter方法给冲掉了，所以得到的是默认的网页文件，我折腾了2天，痛不欲生，刚刚解决掉这个问题，解决方法如下：<BR><BR>1.将下载文件的jsp放到一个特殊的目录下，我的放在download目录，这样可以区分需要被sitemesh装饰和不需要被它装饰的文件。<BR><BR>2.配置sitemesh的decorators.xml:<BR>&lt;decorators defaultdir="/decorators"&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;excludes&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;pattern&gt;/download/*&lt;/pattern&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/excludes&gt; <BR>&lt;/decorators&gt;<BR><BR>这个配置指明download下的文件不需要被sitemesh装饰。<BR><BR><BR><BR>我的下载文件的页面代码是试验代码，稍加修改就可以正式使用，代码如下：<BR>&lt;%@ page import= "java.io.*,java.net.*" %&gt;&lt;%<BR>&nbsp;&nbsp;&nbsp; try{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //取得虚拟的路径<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String fn = "attachment; filename=a.xls";&nbsp; //必须改为UniCode编码的字符串<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(fn);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //把标题、内容写到输出流中<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setHeader("Content-Disposition", new String(fn.getBytes("GB2312"),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "ISO-8859-1"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createOutput( response.getOutputStream(),"c:\\ltf.xls");<BR>&nbsp;&nbsp;&nbsp; }catch( Exception ee ){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ee.printStackTrace();<BR>&nbsp;&nbsp;&nbsp; }<BR>%&gt;&lt;%!<BR>public void createOutput( OutputStream out,String realpath ) throws IOException {<BR>&nbsp;&nbsp;&nbsp; int b;<BR>&nbsp;&nbsp;&nbsp; BufferedInputStream&nbsp; m_input =<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new BufferedInputStream( new&nbsp; FileInputStream(realpath) );<BR>&nbsp;&nbsp;&nbsp; while( (b = m_input.read()) != -1 ){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.write(b);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; m_input.close();<BR>&nbsp;&nbsp;&nbsp; out.flush();<BR>&nbsp;&nbsp;&nbsp; out.close();<BR>}<BR>%&gt;<BR>&lt;%--<BR>&nbsp;&nbsp;// 得到文件名字和路径<BR>&nbsp;&nbsp;String filename = request.getParameter("zipfilename");<BR>&nbsp;&nbsp;//String jsppath = "";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getServletConfig().getServletContext().getRealPath("") + <BR>&nbsp;&nbsp;&nbsp;String filepath = "c:\\ltf.xls";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //jsppath.substring(0,jsppath.lastIndexOf("")) + "reports\\";<BR>&nbsp;&nbsp;System.out.println("---------------=================" + filepath);<BR>&nbsp;&nbsp;java.io.FileInputStream fileInputStream = null;<BR>&nbsp;&nbsp;//out.println(filepath);<BR>&nbsp;&nbsp;// 设置响应头和下载保存的文件名<BR>&nbsp;&nbsp;response.setHeader("Content-Disposition",<BR>&nbsp;&nbsp;"attachment; filename=" + "a.xls");<BR>&nbsp;&nbsp;response.setContentType("application/msexcel");<BR>&nbsp;<BR>&nbsp;&nbsp;// 打开指定文件的流信息<BR>&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;fileInputStream =<BR>&nbsp;&nbsp;&nbsp;new java.io.FileInputStream(filepath);<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;// 写出流信息<BR>&nbsp;&nbsp;int i;<BR>&nbsp;&nbsp;while ((i=fileInputStream.read()) != -1) {<BR>&nbsp;&nbsp;&nbsp;out.write(i);<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;fileInputStream.close();<BR>&nbsp;&nbsp;out.close();<BR>&nbsp;&nbsp;System.out.println("---OK---------------");<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;}catch(Exception ee){<BR>&nbsp;&nbsp;&nbsp; ee.printStackTrace();<BR>&nbsp;&nbsp;}<BR>--%&gt;<BR><img src ="http://www.cnblogs.com/maxpain/aggbug/168369.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47994/" target="_blank">Google号召社区力量为互联网加速</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[转]AppFuse中使用Oracle需要注意的问题</title><link>http://www.cnblogs.com/maxpain/archive/2005/06/05/168367.html</link><dc:creator>数码民工</dc:creator><author>数码民工</author><pubDate>Sun, 05 Jun 2005 09:35:00 GMT</pubDate><guid>http://www.cnblogs.com/maxpain/archive/2005/06/05/168367.html</guid><wfw:comment>http://www.cnblogs.com/maxpain/comments/168367.html</wfw:comment><comments>http://www.cnblogs.com/maxpain/archive/2005/06/05/168367.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/maxpain/comments/commentRss/168367.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/maxpain/services/trackbacks/168367.html</trackback:ping><description><![CDATA[http://www.blogbus.com/public/tb.php/1216089 <BR>Oracle9i带的JRE为1.3,而且安装后会占据环境变量path的第一项，解决办法是将这项移到path的最后一项。<BR><BR><BR>如果使用DBUnit向Oracle中添加数据，如果你的用户的权限是DBA，并且在不同的用户下有同名的表，则DBUnit会不知道该向那个表插入，只要将用户权限降低就行了，另外还要注意有外键的表的插入数据的先后次序。<BR><BR>我的用户的角色是：CONNECT和RESOURCE<BR>系统权限是：UNLIMITED TABLESPACE, <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CREATE TABLE ,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE SESSION,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE TABLESPACE,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE SEQUENCE,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE CLUSTER,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE TRIGGER,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATE VIEW.。<BR><BR>其实这些系统权限可以根据自己的需要定制，如果不需要有创建试图的权限，当然可以不加。<BR><img src ="http://www.cnblogs.com/maxpain/aggbug/168367.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47994/" target="_blank">Google号召社区力量为互联网加速</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item></channel></rss>