﻿<?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-黑灵客栈-随笔分类-J2EE</title><link>http://www.blogjava.net/mstar/category/1423.html</link><description>黑灵的没啥技术含量的技术博客! -&gt; http://zjumty.iteye.com</description><language>zh-cn</language><lastBuildDate>Sun, 23 Jun 2013 19:54:34 GMT</lastBuildDate><pubDate>Sun, 23 Jun 2013 19:54:34 GMT</pubDate><ttl>60</ttl><item><title>在传输层上压缩WebService的请求和响应 </title><link>http://www.blogjava.net/mstar/archive/2013/06/23/400884.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Sun, 23 Jun 2013 13:45:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2013/06/23/400884.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/400884.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2013/06/23/400884.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/400884.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/400884.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 场景				场景是这样的：客户端.NET 3.5应用程序，WCF实现WebService调用， 服务端Java，通过CXF提供WebService。 有一个方法提供了有一个字符串类型的参数，实际生产环境里会传100k以上的字符串。在并发量比较大的情况下，带宽占用很严重。所以寻找一种可以把传输的SOAP消息在客户端压缩，服务端解压缩的方法。这里提供的方式在是客户端通过WCF的...&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2013/06/23/400884.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/400884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2013-06-23 21:45 <a href="http://www.blogjava.net/mstar/archive/2013/06/23/400884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Request Parameter中的空格与加号</title><link>http://www.blogjava.net/mstar/archive/2005/11/07/18594.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Mon, 07 Nov 2005 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/11/07/18594.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/18594.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/11/07/18594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/18594.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/18594.html</trackback:ping><description><![CDATA[<P>今天才知道原来HttpServletRequest.getParameters()对于空格的处理是这样的！<BR><BR>url: xxx?p=a+b<BR>request.getParameters("p"): a b(a与b中间是空格)<BR><BR>url: xxx?p=a b(a与b中间是空格)<BR>request.getParameters("p"): a b(a与b中间是空格)<BR><BR>url: xxx?p=a%20b<BR>request.getParameters("p"): a b(a与b中间是空格)<BR><BR>url: xxx?p=a%2Bb<BR>request.getParameters("p"): a+b<BR><BR>直接用Get请求的时候，如果参数中有+号或者空格一定要看看是不是应该先encode一下。如果是空格用javascript的escape()就能encode，如果是+号就得自己转了。</P><img src ="http://www.blogjava.net/mstar/aggbug/18594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-11-07 16:09 <a href="http://www.blogjava.net/mstar/archive/2005/11/07/18594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个文件下载的Servlet</title><link>http://www.blogjava.net/mstar/archive/2005/10/27/17113.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 27 Oct 2005 14:52:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/10/27/17113.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/17113.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/10/27/17113.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/17113.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/17113.html</trackback:ping><description><![CDATA[<P><FONT size=4>把文件目录直接暴露给用户是很不安全的。所以要用Servlet来做，而且这样做，文件的存储方式就更丰富了，可以是从文件系统上取来的，也可以是数据库中经过计算生成的，或者从其它什么稀奇古怪的地方取来的。<BR></FONT></P>
<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: #0000ff">public</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000"> DownloadServlet </SPAN><SPAN style="COLOR: #0000ff">extends</SPAN><SPAN style="COLOR: #000000"> HttpServlet {<BR>    </SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000"> String contentType </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">application/x-msdownload</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR>    </SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000"> String enc </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">utf-8</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR>    </SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000"> String fileRoot </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">;<BR><BR><BR>    </SPAN><SPAN style="COLOR: #008000">/**</SPAN><SPAN style="COLOR: #008000"><BR>     * 初始化contentType，enc，fileRoot<BR>     </SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000"> init(ServletConfig config) </SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000"> ServletException {<BR>        String tempStr </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> config.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">contentType</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (tempStr </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">&&</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">tempStr.equals(</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">)) {<BR>            contentType </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> tempStr;<BR>        }<BR>        tempStr </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> config.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">enc</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (tempStr </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">&&</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">tempStr.equals(</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">)) {<BR>            enc </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> tempStr;<BR>        }<BR>        tempStr </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> config.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">fileRoot</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (tempStr </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">&&</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">tempStr.equals(</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">)) {<BR>            fileRoot </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> tempStr;<BR>        }<BR>    }<BR><BR>    </SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000"> doGet(HttpServletRequest request, HttpServletResponse response) </SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000"> ServletException, IOException {<BR>        String filepath </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> request.getParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">filepath</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        String fullFilePath </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> fileRoot </SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000"> filepath;<BR>        </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">读取文件</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>        File file </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000"> File(fullFilePath);<BR>        </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">如果文件存在</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (file.exists()) {<BR>            String filename </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> URLEncoder.encode(file.getName(), enc);<BR>            response.reset();<BR>            response.setContentType(contentType);<BR>            response.addHeader(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Content-Disposition</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">, </SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">attachment; filename=\</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000"> + filename + </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: #0000ff">int</SPAN><SPAN style="COLOR: #000000"> fileLength </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> (</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">) file.length();<BR>            response.setContentLength(fileLength);<BR>            </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">如果文件长度大于0</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (fileLength </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">) {<BR>                </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">创建输入流</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>                InputStream inStream </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000"> FileInputStream(file);<BR>                </SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[] buf </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[</SPAN><SPAN style="COLOR: #000000">4096</SPAN><SPAN style="COLOR: #000000">];<BR>                </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">创建输出流</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>                ServletOutputStream servletOS </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> response.getOutputStream();<BR>                </SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000"> readLength;<BR>                </SPAN><SPAN style="COLOR: #0000ff">while</SPAN><SPAN style="COLOR: #000000"> (((readLength </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> inStream.read(buf)) </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">)) {<BR>                    servletOS.write(buf, </SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">, readLength);<BR>                }<BR>                inStream.close();<BR>                servletOS.flush();<BR>                servletOS.close();<BR>            }<BR>        }<BR>    }</SPAN></DIV><BR>web.xml<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: #000000">    </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">download</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">org.mstar.servlet.DownloadServlet</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">fileRoot</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">d:/temp</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">contentType</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">application/x-msdownload</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">enc</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">utf-8</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">download</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">/down</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">></SPAN></DIV><img src ="http://www.blogjava.net/mstar/aggbug/17113.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-10-27 22:52 <a href="http://www.blogjava.net/mstar/archive/2005/10/27/17113.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Axis初学手册</title><link>http://www.blogjava.net/mstar/archive/2005/10/06/14870.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 06 Oct 2005 05:49:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/10/06/14870.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/14870.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/10/06/14870.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/14870.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/14870.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 这些天稍微玩了一下Axis，以前做WebServices都使用JBuilder，感觉做WebServices如此简单，现在自己手动做，原来也是如此简单。高兴之余写一个简单的初学手册，就算是学习成果吧。当然对Axis理解的还不很深，所以错误之处望指点。Axis是一个实现WebService的Framework，Apache Web Services Project（http://ws.apach...&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2005/10/06/14870.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/14870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-10-06 13:49 <a href="http://www.blogjava.net/mstar/archive/2005/10/06/14870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对于业务逻辑中发生的异常应该怎样显示到Web层？</title><link>http://www.blogjava.net/mstar/archive/2005/09/27/14240.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 27 Sep 2005 13:55:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/27/14240.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/14240.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/27/14240.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/14240.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/14240.html</trackback:ping><description><![CDATA[<SPAN class=postbody>刚进公司，参加了两个项目一个是日本外包，一个是自己的产品。这个两个系统在设计上都有一个要求，就是业务逻辑要放到Service层来完成，Action中尽量少写东西。这一点可以理解，但是存在这样一个问题，在service中进行业务逻辑处理时免不了要发生问题，要把这些问题显示在页面上，但是service层不能直接接触到action，所以着这两个项目中的设计方法上使吧actionmessages对象给了service。我认为这样做很不好，这样service对表现曾的依赖就太大了，因为ActionMessages是Struts的东西，也就是说这个serivce只能用来对Struts提供服务了。我认为应该在service曾定义好异常情况，遇到问题时抛出异常（RuntimeException），然后再表现层根据异常种类做出相应的动作。这样就是view层依赖于serivce层，而service层可以为更多种view层提供服务了。但是这样也有一些问题，就是如果serivce逻辑复杂，可能异常种类就非常多，这样项目做起来也很麻烦。 但是刚进公司也没有什么发言权，估计前辈们也知道这样做不好，但是估计是为了项目做起来不会太麻烦吧。<BR>大家有没有更好的办法？</SPAN><img src ="http://www.blogjava.net/mstar/aggbug/14240.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-27 21:55 <a href="http://www.blogjava.net/mstar/archive/2005/09/27/14240.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何实现由状态WebService，以及身份认证如何实现？ </title><link>http://www.blogjava.net/mstar/archive/2005/09/23/13870.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 23 Sep 2005 14:24:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/23/13870.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/13870.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/23/13870.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/13870.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/13870.html</trackback:ping><description><![CDATA[以前做过一点WebService但是都是一些简单的操作，都是没有状态的，有点像stateless session bean但是。现在想要Webservice是有状态的，如记录访问者信息，进行连续的业务方法调用等等。并且如果暴露的Webservice有重要操作，只有经过验证的用户才能调用，如何实现？<BR>大家有什么看法，谈一谈。<BR><img src ="http://www.blogjava.net/mstar/aggbug/13870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-23 22:24 <a href="http://www.blogjava.net/mstar/archive/2005/09/23/13870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用applet + xml-rpc + hsqldb + xml实现一个聊天室</title><link>http://www.blogjava.net/mstar/archive/2005/09/23/13795.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 22 Sep 2005 16:22:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/23/13795.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/13795.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/23/13795.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/13795.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/13795.html</trackback:ping><description><![CDATA[<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4>用</FONT></SPAN><FONT size=4><SPAN lang=EN-US>applet + xml-rpc + hsqldb + xml</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现一个聊天室</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">昨天公司前辈看我没什么事就托我给他做个小聊天室，估计是别人托他做的</SPAN><SPAN lang=EN-US style="FONT-FAMILY: Wingdings; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">L</SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，某个在校学生的作业（其实我也刚从学校里出来，呵呵）。没办法，做吧，其实前辈给了我一个网上下的源程序让我改一下。我看了看，其中有些错误，改了以后，前辈又说要加些功能，可是原来的那个源码使用</SPAN><SPAN lang=EN-US>socket</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">做的，要实现这样的功能，有些麻烦，功能其实很简单，不如自己做个玩玩。于是想到了，以前看到的一些技术和类库，就当练练手吧。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">首先要解决的问题是</SPAN><SPAN lang=EN-US>applet</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如何与</SPAN><SPAN lang=EN-US>server</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">进行通信。发放有很多，但是对于这样的小东西，最好用一个轻量级的实现起来简单的技术，于是我想到了</SPAN><SPAN lang=EN-US>xml-rpc</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，以前做个用</SPAN><SPAN lang=EN-US>javascript</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">通过</SPAN><SPAN lang=EN-US>xml-rpc</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">与服务端通信（我以前的文章中写过），现在客户端也是</SPAN><SPAN lang=EN-US>Java</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现起来就容易多了。</SPAN><SPAN lang=EN-US>Xml-rpc</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是个技术规范，有很多实现，现在客户端和服务器都用</SPAN><SPAN lang=EN-US>Java</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，当然要用</SPAN><SPAN lang=EN-US>apache</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的实现。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接下来要解决的问题是用户数据库如何实现，这样一个小程序就不用精通</SPAN><SPAN lang=EN-US>mysql</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</SPAN><SPAN lang=EN-US>sqlserver</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的“大驾”了。本想用</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">一起实现的了，但是想了想，不易管理，用户要想更改用户，</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">没有一个很好的工具，而且，用户数据库，其实就是一张用户表，有没有大的访问量，完全可以用</SPAN><SPAN lang=EN-US>xml</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来储存。于是我还是决定用</SPAN><SPAN lang=EN-US>dom4j</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来操作揖个</SPAN><SPAN lang=EN-US>user.xml</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来实现。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">下面是聊天信息的中转，和在线状态的维护。其实这个自己写一个类来实现也可以，无非就是维护一个</SPAN><SPAN lang=EN-US>map</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，但是想到自己还是没有那么高的水平一定能把这个做好，莫不如用内存数据库来实现，效率也不错，操作也方便。这时</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">就派上用场了。其实</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的用途还是很大的。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT size=4>好了下面是总体框架<BR></FONT><IMG height=625 alt=design.jpg src="http://www.blogjava.net/images/blogjava_net/mstar/pics/design.jpg" width=568 border=0><BR></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对了，还有一个</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">monitor</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，是用来维护用户在线状态的，因为用户很有可能不是正常退出，所以这个</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">monitor</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">作为一个单独的线程对超时用户进行处理。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN lang=EN-US><FONT face="Times New Roman">Service</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">ChatService.java</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）是主业务类，其实它也只是一个代理类，实际业务是由</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">ChatEngine.java</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">hsql</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）和</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">XmlHelper.java</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">xml</FONT></SPAN></FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT size=4>）完成的。<BR></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT size=4>先看一下截图吧<BR></FONT><IMG height=439 alt=appletrpc.jpg src="http://www.blogjava.net/images/blogjava_net/mstar/pics/appletrpc.jpg" width=495 border=0><BR><FONT size=4>下面详细的介绍一下，每部分的实现</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4>首先是业务逻辑类ChatService.java<BR></FONT></P>
<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: #0000ff">package</SPAN><SPAN style="COLOR: #000000">&nbsp;org.mstar.appletrpc.rpcserver;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;java.util.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatService&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlHelper&nbsp;helper;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatEngine&nbsp;engine;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatService(XmlHelper&nbsp;helper,ChatEngine&nbsp;engine){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.helper&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;helper;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.engine&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;engine;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;validateUser(String&nbsp;username,String&nbsp;password){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User&nbsp;user&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;helper.getUser(username);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(user</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">&amp;&amp;</SPAN><SPAN style="COLOR: #000000">user.getPassword()</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">&amp;&amp;</SPAN><SPAN style="COLOR: #000000">user.getPassword().equals(password)){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">true</SPAN><SPAN style="COLOR: #000000">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">false</SPAN><SPAN style="COLOR: #000000">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;existUser(String&nbsp;username){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;helper.existUser(username);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;addUser(String&nbsp;username,String&nbsp;password,String&nbsp;nickname){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User&nbsp;user&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;User();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.setUsername(username);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.setPassword(password);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.setNickname(nickname);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;helper.addUser(user);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;addMessage(String&nbsp;sender,&nbsp;String&nbsp;receiver,&nbsp;String&nbsp;content)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.addMessage(sender,receiver,content);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;Map&nbsp;getMessage(String&nbsp;receiver)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.getMessage(receiver);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;logon(String&nbsp;user){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.logon(user);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;Vector&nbsp;getOnline(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.getOnline();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;logoff(String&nbsp;user){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.logoff(user);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">/**</SPAN><SPAN style="COLOR: #008000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;checkOnline<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;checkOnline()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine.checkOnline();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR></SPAN></DIV><FONT size=4>这个类是个很普通的类，为底层的业务逻辑实现者做个代理。这里要注意的是由于xml-rpc支持的Type很有限，所以，很多是由返回值得类型要注意一下，数组要用Vector，对象要用HashTable或Map，不能直接返回一个复杂对象类型，如User之类的。<BR>具体实现类就不说了，不复杂，看源码应该看得懂。<BR>写完Service，要做一个RpcServer,其实是个Servlet<BR>RpcServer.java</FONT> 
<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: #0000ff">package</SPAN><SPAN style="COLOR: #000000">&nbsp;org.mstar.appletrpc.rpcserver;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;javax.servlet.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;javax.servlet.http.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;java.io.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;org.apache.xmlrpc.XmlRpcServer;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;org.dom4j.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;java.sql.SQLException;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;RpcServer&nbsp;</SPAN><SPAN style="COLOR: #0000ff">extends</SPAN><SPAN style="COLOR: #000000">&nbsp;HttpServlet&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;ServletConfig&nbsp;servletConfig;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlHelper&nbsp;xmlHelper;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatEngine&nbsp;engine;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlRpcServer&nbsp;xmlrpc;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">Initialize&nbsp;global&nbsp;variables</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;init(ServletConfig&nbsp;servletConfig)&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000">&nbsp;ServletException&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.servletConfig&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;servletConfig;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;userdata&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;servletConfig.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">user-data</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;file&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;File(servletConfig.getServletContext().getRealPath(userdata));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlHelper&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlHelper(file);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatEngine();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChatService&nbsp;service&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatService(xmlHelper,engine);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlRpcServer();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlrpc.addHandler(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,service);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;OnlineMonitor(service).start();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(DocumentException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(SQLException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(ClassNotFoundException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">Process&nbsp;the&nbsp;HTTP&nbsp;Post&nbsp;request</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;doPost(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ServletException,&nbsp;IOException&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[]&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc.execute(request.getInputStream());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setContentType(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">text/xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setContentLength(result.length);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OutputStream&nbsp;out&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;response.getOutputStream();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.write(result);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.flush();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">Clean&nbsp;up&nbsp;resources</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;destroy()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR></SPAN></DIV></SPAN><FONT size=4>这个类主要做两件事，一是初始化一些业务对象如</FONT><FONT face=宋体><FONT size=4>ChatService和XmlHelper，ChatEngine，启动monitor。<BR>二是将Service放到XmlRpcServer中。使客户端通过xml客户远程调用。<BR>然后就是把这个Servlet注册到web.xml中<BR></FONT>
<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: #0000ff"><?</SPAN><SPAN style="COLOR: #ff00ff">xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"</SPAN><SPAN style="COLOR: #0000ff">?&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">web-app&nbsp;</SPAN><SPAN style="COLOR: #ff0000">xmlns</SPAN><SPAN style="COLOR: #0000ff">="http://java.sun.com/xml/ns/j2ee"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;xmlns:xsi</SPAN><SPAN style="COLOR: #0000ff">="http://www.w3.org/2001/XMLSchema-instance"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;xsi:schemaLocation</SPAN><SPAN style="COLOR: #0000ff">="http://java.sun.com/xml/ns/j2ee&nbsp;http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;version</SPAN><SPAN style="COLOR: #0000ff">="2.4"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">display-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">AppRpc</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">display-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">welcome-file-list</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">welcome-file</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">index.jsp</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">welcome-file</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">welcome-file-list</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">RpcServer</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">org.mstar.appletrpc.rpcserver.RpcServer</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">user-data</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">/WEB-INF/user.xml</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">load-on-startup</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">load-on-startup</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">RpcServer</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">/xmlrpc</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">web-app</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV><BR><FONT size=4>还要有个用户数据库user.xml<BR></FONT>
<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: #0000ff"><?</SPAN><SPAN style="COLOR: #ff00ff">xml&nbsp;version="1.0"&nbsp;encoding="GBK"</SPAN><SPAN style="COLOR: #0000ff">?&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user-database</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty1"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一1"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty2"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一2"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty3"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一3"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty4"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一4"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">user-database</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN></DIV><FONT size=4>注意user.xml的路径是在web.xml中设定的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></FONT><SPAN style="COLOR: #000000"><BR><FONT size=4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT></SPAN><FONT size=4><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">user-data</SPAN></FONT><SPAN style="COLOR: #0000ff"></< SPAN><FONT size=4><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></FONT><SPAN style="COLOR: #000000"><BR><FONT size=4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT></SPAN><FONT size=4><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">/WEB-INF/user.xml</SPAN></FONT><SPAN style="COLOR: #0000ff"></< SPAN><FONT size=4><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></FONT><SPAN style="COLOR: #000000"><BR><FONT size=4>&nbsp;&nbsp;&nbsp;&nbsp;</FONT></SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000"><FONT size=4>init-param</FONT></SPAN><SPAN style="COLOR: #0000ff"><FONT size=4>&gt;<BR>接下来就是用Applet调用这个Servlet了。<BR>太长了，就不写在页面上了，源码中有，这里写一个例子，登陆的actionPerformed方法</FONT><BR><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">
<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: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Vector&nbsp;params&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Vector();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params.addElement(useridTextField.getText());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params.addElement(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;String(pwdField.getPassword()));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boolean&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Boolean(</SPAN><SPAN style="COLOR: #0000ff">false</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XmlRpcClient&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlRpcClient(getCodeBase().toString()&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000"><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;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">xmlrpc</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(Boolean)&nbsp;xmlrpc.execute(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService.validateUser</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(result.booleanValue())&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Vector();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params.addElement(useridTextField.getText());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(Boolean)&nbsp;xmlrpc.execute(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService.logon</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,<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;params);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(result.booleanValue())&nbsp;{<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;userid&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;useridTextField.getText();<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;appendMessage(userid&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">：&nbsp;您已经登陆成功</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<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;resetUserList();<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;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Timer(</SPAN><SPAN style="COLOR: #000000">2000</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;RefreshMessageActionListener()).<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;start();<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;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Timer(</SPAN><SPAN style="COLOR: #000000">5000</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;RefreshListActionListener()).<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;start();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendMessage(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">用户名密码错误，登陆失败</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(IOException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(XmlRpcException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></DIV>
<P><FONT color=#000000><FONT size=4>xmlrpc.execute(<SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService.validateUser</SPAN><SPAN style="COLOR: #000000">"</SPAN></FONT></FONT><SPAN style="COLOR: #000000"><FONT size=4>,params);<BR>execute方法是xml-rpc client执行远程调用的方法。<BR>第一个参数是方法名，第二个是这个远程方法的参数，用Vector传递。<BR>基本上就是这样一个流程。一点都不复杂，有什么不明白的就给我发信，欢迎讨论。<BR>源码下载：</FONT><A href="http://www.blogjava.net/mstar/mstar/Files/mstar/AppletRPC.rar"><FONT size=4>http://www.blogjava.net/Files/mstar/AppletRPC.rar</FONT></A><BR><FONT size=4>项目是用JBuilder2006做的，不知道以前版本的JBuilder能不能打开啊。</FONT></SPAN></SPAN></SPAN></FONT></P></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN><img src ="http://www.blogjava.net/mstar/aggbug/13795.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-23 00:22 <a href="http://www.blogjava.net/mstar/archive/2005/09/23/13795.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Java 理论与实践: 用动态代理进行修饰</title><link>http://www.blogjava.net/mstar/archive/2005/09/22/13790.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 22 Sep 2005 14:25:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/22/13790.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/13790.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/22/13790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/13790.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/13790.html</trackback:ping><description><![CDATA[<P id=subtitle><SPAN style="COLOR: #999999"><A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html">http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html</A><BR>Java 理论与实践: </SPAN>用动态代理进行修饰<BR>动态代理是构建 Decorator 和 Adapter 的方便工具<BR></P>
<BLOCKQUOTE><I>动态代理工具</I> 是 <CODE>java.lang.reflect</CODE> 包的一部分，在 JDK 1.3 版本中添加到 JDK，它允许程序创建 <I>代理对象</I>，代理对象能实现一个或多个已知接口，并用反射代替内置的虚方法分派，编程地分派对接口方法的调用。这个过程允许实现“截取”方法调用，重新路由它们或者动态地添加功能。本期文章中，Brian Goetz 介绍了几个用于动态代理的应用程序。请在本文伴随的 <A href="javascript:void forumWindow()"><FONT color=#5c81a7>讨论论坛</FONT></A> 上与作者和其他读者分享您对这篇文章的想法。（也可以单击文章顶部或底部的 <B>讨论</B> 访问讨论论坛。）</BLOCKQUOTE>
<P>动态代理为实现许多常见设计模式（包括 Facade、Bridge、Interceptor、Decorator、Proxy（包括远程和虚拟代理）和 Adapter 模式）提供了替代的动态机制。虽然这些模式不使用动态代理，只用普通的类就能够实现，但是在许多情况下，动态代理方式更方便、更紧凑，可以清除许多手写或生成的类。</P>
<P><A name=1.0><SPAN class=atitle><FONT face=Arial size=4>Proxy 模式</FONT></SPAN></A></P>
<P>Proxy 模式中要创建“stub”或“surrogate”对象，它们的目的是接受请求并把请求转发到实际执行工作的其他对象。远程方法调用（RMI）利用 Proxy 模式，使得在其他 JVM 中执行的对象就像本地对象一样；企业 JavaBeans （EJB）利用 Proxy 模式添加远程调用、安全性和事务分界；而 JAX-RPC Web 服务则用 Proxy 模式让远程服务表现得像本地对象一样。在每一种情况中，潜在的远程对象的行为是由接口定义的，而接口本质上接受多种实现。调用者（在大多数情况下）不能区分出它们只是持有一个对 stub 而不是实际对象的引用，因为二者实现了相同的接口；stub 的工作是查找实际的对象、封送参数、把参数发送给实际对象、解除封送返回值、把返回值返回给调用者。代理可以用来提供远程控制（就像在 RMI、EJB 和 JAX-RPC 中那样），用安全性策略包装对象（EJB）、为昂贵的对象（EJB 实体 Bean）提供惰性装入，或者添加检测工具（例如日志记录）。</P>
<P>在 5.0 以前的 JDK 中，RMI stub（以及它对等的 skeleton）是在编译时由 RMI 编译器（rmic）生成的类，RMI 编译器是 JDK 工具集的一部分。对于每个远程接口，都会生成一个 stub（代理）类，它代表远程对象，还生成一个 skeleton 对象，它在远程 JVM 中做与 stub 相反的工作 —— 解除封送参数并调用实际的对象。类似地，用于 Web 服务的 JAX-RPC 工具也为远程 Web 服务生成代理类，从而使远程 Web 服务看起来就像本地对象一样。</P>
<P>不管 stub 类是以源代码还是以字节码生成的，代码生成仍然会向编译过程添加一些额外步骤，而且因为命名相似的类的泛滥，会带来意义模糊的可能性。另一方面，动态代理机制支持在编译时没有生成 stub 类的情况下，在运行时创建代理对象。在 JDK 5.0 及以后版本中，RMI 工具使用动态代理代替了生成的 stub，结果 RMI 变得更容易使用。许多 J2EE 容器也使用动态代理来实现 EJB。EJB 技术严重地依靠使用拦截（interception）来实现安全性和事务分界；动态代理为接口上调用的所有方法提供了集中的控制流程路径。</P>
<P id=subtitle><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD></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><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR></P>
<P><A name=2.0><SPAN class=atitle><FONT face=Arial size=4>动态代理机制</FONT></SPAN></A></P>
<P>动态代理机制的核心是 <CODE>InvocationHandler</CODE> 接口，如清单 1 所示。调用句柄的工作是代表动态代理实际执行所请求的方法调用。传递给调用句柄一个 <CODE>Method</CODE> 对象（从 <CODE>java.lang.reflect</CODE> 包），参数列表则传递给方法；在最简单的情况下，可能仅仅是调用反射性的方法 <CODE>Method.invoke()</CODE> 并返回结果。</P>
<P id=subtitle><BR><A name=listing1><B>清单 1. InvocationHandler 接口</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">public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>每个代理都有一个与之关联的调用句柄，只要代理的方法被调用时就会调用该句柄。根据通用的设计原则：接口定义类型、类定义实现，代理对象可以实现一个或多个接口，但是不能实现类。因为代理类没有可以访问的名称，它们不能有构造函数，所以它们必须由工厂创建。清单 2 显示了动态代理的最简单的可能实现，它实现 <CODE>Set</CODE> 接口并把所有 <CODE>Set</CODE> 方法（以及所有 Object 方法）分派给封装的 <CODE>Set</CODE> 实例。</P>
<P id=subtitle><BR><A name=listing2><B>清单 2. 包装 Set 的简单的动态代理</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">public class SetProxyFactory {

    public static Set getSetProxy(final Set s) {
        return (Set) Proxy.newProxyInstance
          (s.getClass().getClassLoader(),
                new Class[] { Set.class },
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, 
                      Object[] args) throws Throwable {
                        return method.invoke(s, args);
                    }
                });
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P><CODE>SetProxyFactory</CODE> 类包含一个静态工厂方法 <CODE>getSetProxy()</CODE>，它返回一个实现了 <CODE>Set</CODE> 的动态代理。代理对象实际实现 <CODE>Set</CODE> —— 调用者无法区分（除非通过反射）返回的对象是动态代理。<CODE>SetProxyFactory</CODE> 返回的代理只做一件事，把方法分派给传递给工厂方法的 <CODE>Set</CODE> 实例。虽然反射代码通常比较难读，但是这里的内容很少，跟上控制流程并不难 —— 只要某个方法在 <CODE>Set</CODE> 代理上被调用，它就被分派给调用句柄，调用句柄只是反射地调用底层包装的对象上的目标方法。当然，绝对什么都不做的代理可能有点傻，是不是呢？</P>
<P><A name=2.1><SPAN class=smalltitle><STRONG><FONT face=Arial>什么都不做的适配器</FONT></STRONG></SPAN></A></P>
<P>对于像 <CODE>SetProxyFactory</CODE> 这样什么都不做的包装器来说，实际有个很好的应用 —— 可以用它安全地把对象引用的范围缩小到特定接口（或接口集）上，方式是，调用者不能提升引用的类型，使得可以更安全地把对象引用传递给不受信任的代码（例如插件或回调）。清单 3 包含一组类定义，实现了典型的回调场景。从中会看到动态代理可以更方便地替代通常用手工（或用 IDE 提供的代码生成向导）实现的 Adapter 模式。</P>
<P id=subtitle><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">public interface ServiceCallback {
    public void doCallback();
}

public interface Service {
    public void serviceMethod(ServiceCallback callback);
}

public class ServiceConsumer implements ServiceCallback {
    private Service service;

    ...
    public void someMethod() {
        ...
        service.serviceMethod(this);
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P><CODE>ServiceConsumer</CODE> 类实现了 <CODE>ServiceCallback</CODE>（这通常是支持回调的一个方便途径）并把 <CODE>this</CODE> 引用传递给 <CODE>serviceMethod()</CODE> 作为回调引用。这种方法的问题是没有机制可以阻止 <CODE>Service</CODE> 实现把 <CODE>ServiceCallback</CODE> 提升为 <CODE>ServiceConsumer</CODE>，并调用 <CODE>ServiceConsumer</CODE> 不希望 <CODE>Service</CODE> 调用的方法。有时对这个风险并不关心 —— 但有时却关心。如果关心，那么可以把回调对象作为内部类，或者编写一个什么都不做的适配器类（请参阅清单 4 中的 <CODE>ServiceCallbackAdapter</CODE>）并用 <CODE>ServiceCallbackAdapter</CODE> 包装 <CODE>ServiceConsumer</CODE>。<CODE>ServiceCallbackAdapter</CODE> 防止 <CODE>Service</CODE> 把 <CODE>ServiceCallback</CODE> 提升为 <CODE>ServiceConsumer</CODE>。</P>
<P id=subtitle><BR><A name=listing4><B>清单 4. 用于安全地把对象限制在一个接口上以便不被恶意代码不能的适配器类</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">public class ServiceCallbackAdapter implements ServiceCallback {
    private final ServiceCallback cb;

    public ServiceCallbackAdapter(ServiceCallback cb) {
        this.cb = cb;
    }

    public void doCallback() {
        cb.doCallback();
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>编写 <CODE>ServiceCallbackAdapter</CODE> 这样的适配器类简单却乏味。必须为包装的接口中的每个方法编写重定向类。在 <CODE>ServiceCallback</CODE> 的示例中，只有一个需要实现的方法，但是某些接口，例如 Collections 或 JDBC 接口，则包含许多方法。现代的 IDE 提供了“Delegate Methods”向导，降低了编写适配器类的工作量，但是仍然必须为每个想要包装的接口编写一个适配器类，而且对于只包含生成的代码的类，也有一些让人不满意的地方。看起来应当有一种方式可以更紧凑地表示“什么也不做的限制适配器模式”。</P>
<P><A name=2.2><SPAN class=smalltitle><STRONG><FONT face=Arial>通用适配器类</FONT></STRONG></SPAN></A></P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#listing2"><FONT color=#996699>清单 2</FONT></A> 中的 <CODE>SetProxyFactory</CODE> 类当然比用于 <CODE>Set</CODE> 的等价的适配器类更紧凑，但是它仍然只适用于一个接口：<CODE>Set</CODE>。但是通过使用泛型，可以容易地创建通用的代理工厂，由它为任何接口做同样的工作，如清单 5 所示。它几乎与 <CODE>SetProxyFactory</CODE> 相同，但是可以适用于任何接口。现在再也不用编写限制适配器类了！如果想创建代理对象安全地把对象限制在接口 <CODE>T</CODE>，只要调用 <CODE>getProxy(T.class,object)</CODE> 就可以了，不需要一堆适配器类的额外累赘。</P>
<P id=subtitle><BR><A name=listing5><B>清单 5. 通用的限制适配器工厂类</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">public class GenericProxyFactory {

    public static&lt;T&gt; T getProxy(Class&lt;T&gt; intf, 
      final T obj) {
        return (T) 
          Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                new Class[] { intf },
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, 
                      Object[] args) throws Throwable {
                        return method.invoke(obj, args);
                    }
                });
    }
}
</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-jtp08305.html#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR></P>
<P><A name=3.0><SPAN class=atitle><FONT face=Arial size=4>动态代理作为 Decorator</FONT></SPAN></A></P>
<P>当然，动态代理工具能做的，远不仅仅是把对象类型限制在特定接口上。从 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#listing2"><FONT color=#996699>清单 2</FONT></A> 和 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#listing5"><FONT color=#996699>清单 5</FONT></A> 中简单的限制适配器到 Decorator 模式，是一个小的飞跃，在 Decorator 模式中，代理用额外的功能（例如安全检测或日志记录）包装调用。清单 6 显示了一个日志 <CODE>InvocationHandler</CODE>，它在调用目标对象上的方法之外，还写入一条日志信息，显示被调用的方法、传递的参数，以及返回值。除了反射性的 <CODE>invoke()</CODE> 调用之外，这里的全部代码只是生成调试信息的一部分 —— 还不是太多。代理工厂方法的代码几乎与 <CODE>GenericProxyFactory</CODE> 相同，区别在于它使用的是 <CODE>LoggingInvocationHandler</CODE> 而不是匿名的调用句柄。</P>
<P id=subtitle><BR><A name=listing6><B>清单 6. 基于代理的 Decorator，为每个方法调用生成调试日志</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">    private static class LoggingInvocationHandler&lt;T&gt; 
      implements InvocationHandler {
        final T underlying;

        public LoggingHandler(T underlying) {
            this.underlying = underlying;
        }

        public Object invoke(Object proxy, Method method, 
          Object[] args) throws Throwable {
            StringBuffer sb = new StringBuffer();
            sb.append(method.getName()); sb.append("(");
            for (int i=0; args != null &amp;&amp; i&lt;args.length; i++) {
                if (i != 0)
                    sb.append(", ");
                sb.append(args[i]);
            }
            sb.append(")");
            Object ret = <SPAN class=boldcode><STRONG>method.invoke</STRONG></SPAN>(underlying, args);
            if (ret != null) {
                sb.append(" -&gt; "); sb.append(ret);
            }
            System.out.println(sb);
            return ret;
        }
    }
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>如果用日志代理包装 <CODE>HashSet</CODE>，并执行下面这个简单的测试程序：</P>
<P id=subtitle>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">    Set s = newLoggingProxy(Set.class, new HashSet());
    s.add("three");
    if (!s.contains("four"))
        s.add("four");
    System.out.println(s);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>会得到以下输出：</P>
<P id=subtitle>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">  add(three) -&gt; true
  contains(four) -&gt; false
  add(four) -&gt; true
  toString() -&gt; [four, three]
  [four, three]
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>这种方式是给对象添加调试包装器的一种好的而且容易的方式。它当然比生成代理类并手工创建大量 <CODE>println()</CODE> 语句容易得多（也更通用）。我进一步改进了这一方法；不必无条件地生成调试输出，相反，代理可以查询动态配置存储（从配置文件初始化，可以由 JMX <CODE>MBean</CODE> 动态修改），确定是否需要生成调试语句，甚至可能在逐个类或逐个实例的基础上进行。</P>
<P>在这一点上，我认为读者中的 AOP 爱好者们几乎要跳出来说“这正是 AOP 擅长的啊！”是的，但是解决问题的方法不止一种 —— 仅仅因为某项技术能解决某个问题，并不意味着它就是最好的解决方案。在任何情况下，动态代理方式都有完全在“纯 Java”范围内工作的优势，不是每个公司都用（或应当用） AOP 的。</P>
<P><A name=3.1><SPAN class=smalltitle><STRONG><FONT face=Arial>动态代理作为适配器</FONT></STRONG></SPAN></A></P>
<P>代理也可以用作真正的适配器，提供了对象的一个视图，导出与底层对象实现的接口不同的接口。调用句柄不需要把每个方法调用都分派给相同的底层对象；它可以检查名称，并把不同的方法分派给不同的对象。例如，假设有一组表示持久实体（<CODE>Person</CODE>、<CODE>Company</CODE> 和 <CODE>PurchaseOrder</CODE>） 的 JavaBean 接口，指定了属性的 getter 和 setter，而且正在编写一个持久层，把数据库记录映射到实现这些接口的对象上。现在不用为每个接口编写或生成类，可以只用一个 JavaBean 风格的通用代理类，把属性保存在 Map 中。</P>
<P>清单 7 显示的动态代理检查被调用方法的名称，并通过查询或修改属性图直接实现 getter 和 setter 方法。现在，这一个代理类就能实现多个 JavaBean 风格接口的对象。</P>
<P id=subtitle><BR><A name=listing7><B>清单 7. 用于把 getter 和 setter 分派给 Map 的动态代理类</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">public class JavaBeanProxyFactory {
    private static class JavaBeanProxy implements InvocationHandler {
        Map&lt;String, Object&gt; properties = new HashMap&lt;String, 
          Object&gt;();

        public JavaBeanProxy(Map&lt;String, Object&gt; properties) {
            this.properties.putAll(properties);
        }

        public Object invoke(Object proxy, Method method, 
          Object[] args) 
          throws Throwable {
            String meth = method.getName();
            if (meth.startsWith("get")) {
                String prop = meth.substring(3);
                Object o = properties.get(prop);
                if (o != null &amp;&amp; !method.getReturnType().isInstance(o))
                    throw new ClassCastException(o.getClass().getName() + 
                      " is not a " + method.getReturnType().getName());
                return o;
            }
            else if (meth.startsWith("set")) {
                // Dispatch setters similarly
            }
            else if (meth.startsWith("is")) {
                // Alternate version of get for boolean properties
            }
            else {
                // Can dispatch non get/set/is methods as desired
            }
        }
    }

    public static&lt;T&gt; T getProxy(Class&lt;T&gt; intf,
      Map&lt;String, Object&gt; values) {
        return (T) Proxy.newProxyInstance
          (JavaBeanProxyFactory.class.getClassLoader(),
                new Class[] { intf }, new JavaBeanProxy(values));
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>虽然因为反射在 <CODE>Object</CODE> 上工作会有潜在的类型安全性上的损失，但是，JavaBeanProxyFactory 中的 getter 处理会进行一些必要的额外的类型检测，就像我在这里用 <CODE>isInstance()</CODE> 对 getter 进行的检测一样。</P>
<P id=subtitle><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-jtp08305.html#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR></P>
<P><A name=4.0><SPAN class=atitle><FONT face=Arial size=4>性能成本</FONT></SPAN></A></P>
<P>正如已经看到的，动态代理拥有简化大量代码的潜力 —— 不仅能替代许多生成的代码，而且一个代理类还能代替多个手写的类或生成的代码。什么是成本呢？ 因为反射地分派方法而不是采用内置的虚方法分派，可能有一些性能上的成本。在早期的 JDK 中，反射的性能很差（就像早期 JDK 中几乎其他每件事的性能一样），但是在近 10 年，反射已经变得快多了。</P>
<P>不必进入基准测试构造的主题，我编写了一个简单的、不太科学的测试程序，它循环地把数据填充到 <CODE>Set</CODE>，随机地对 <CODE>Set</CODE>进行插入、查询和删除元素。我用三个 <CODE>Set</CODE> 实现运行它：一个未经修饰的 <CODE>HashSet</CODE>，一个手写的、只是把所有方法转发到底层的 <CODE>HashSet</CODE> 的 <CODE>Set</CODE> 适配器，还有一个基于代理的、也只是把所有方法转发到底层 <CODE>HashSet</CODE> 的 <CODE>Set</CODE> 适配器。每次循环迭代都生成若干随机数，并执行一个或多个 <CODE>Set</CODE> 操作。手写的适配器比起原始的 <CODE>HashSet</CODE> 只产生很少百分比的性能负荷（大概是因为 JVM 级有效的内联缓冲和硬件级的分支预测）；代理适配器则明显比原始 <CODE>HashSet</CODE> 慢，但是开销要少于两个量级。</P>
<P>我从这个试验得出的结论是：对于大多数情况，代理方式即使对轻量级方法也执行得足够好，而随着被代理的操作变得越来越重量级（例如远程方法调用，或者使用序列化、执行 IO 或者从数据库检索数据的方法），代理开销就会有效地接近于 0。当然也存在一些代理方式的性能开销无法接受的情况，但是这些通常只是少数情况。</P><img src ="http://www.blogjava.net/mstar/aggbug/13790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-22 22:25 <a href="http://www.blogjava.net/mstar/archive/2005/09/22/13790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Classworking 工具箱: 在旧版 JVM 上使用 J2SE 5.0 特性</title><link>http://www.blogjava.net/mstar/archive/2005/07/28/8590.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 28 Jul 2005 00:53:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/28/8590.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/8590.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/28/8590.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/8590.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/8590.html</trackback:ping><description><![CDATA[<SPAN class=atitle2><A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java">http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java</A><BR>无法转向 JDK 5.0？学习一款开放源代码工具如何帮助在旧版 JVM 上使用这些特性</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/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#author1"><NAME>Dennis Sosnoski</NAME></A><BR>主席, Sosnoski Software Solutions, Inc.<BR>2005 年 7 月 25 日</P>
<BLOCKQUOTE>许多 J2SE 5.0 语言特性应该对旧版 JVM 也有用，但是实现这些特性的编译器会生成需要 JDK 5.0 或更高版本的代码。幸运的是，有一个开放源代码项目 Retroweaver 在 J2SE 5.0 与旧版 JVM 之间架起了一座桥梁。Retroweaver 转换您的类文件以消除 JDK 5.0 依赖性，同时添加其自己的支持函数库以使得大多数 5.0 特性在旧版 JVM 上完全有用。如果您喜欢 J2SE 5.0 语言特性，却无法在运行时使用 JDK 5.0，那么 Retroweaver 就是您所需要的。</BLOCKQUOTE>
<P>J2SE 5.0 为 Java 语言带来了巨大的改变，因此即使是经验丰富的 Java 开发人员也需要深入的培训才能利用 5.0 特性。不幸的是，实现这些语言特性的 JDK 5.0 编译器在生成特定于 JDK 5.0 或更高版本的代码时不支持这些特性。如果试图在早期的 JVM 上运行生成的代码，将会得到 <CODE>java.lang.UnsupportedClassVersionError</CODE> 错误。</P>
<P>即使生成的类指定 JDK 5.0 和更高的 JVM，但是故事并没有结束。开发人员发现，一些新特性实际上生成与旧版 JVM 完全兼容的代码，而其他特性可以与标准库的少量扩展兼容。有一个名叫 Toby Reyelts 的开发人员决定消除 JDK 5.0 编译器限制。结果就是开放源代码的 Retroweaver 项目（参见 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。Retroweaver 使用 classworking 技术来修改由 JDK 5.0 编译器生成的二进制类表示，以便这些类可以与早期的 JVM 一起使用。</P>
<P>对于本文来说，我将展示 Retroweaver 的基本使用。Retroweaver 实际上非常容易使用，所以不用花太大的篇幅去介绍它，所以我还将修改 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 介绍的 annotations+ASM 运行时代码生成方法以使用 5.0 之前的 JDK，期间使用了 Retroweaver 来回避 JDK 5.0 编译器限制。</P>
<P><A name=h1><SPAN class=atitle2>向后兼容 J2SE 5.0</SPAN></A><BR>Retroweaver 包含两个逻辑组件：一个字节码增强器和一个运行时库。字节码增强器使用 classworking 技术来修改由 JDK 5.0 编译器生成的类文件，使得这些类可以用于旧版 JVM。作为类文件修改的一部分，Retroweaver 可能需要替换对添加到 J2SE 5.0 中的标准类的引用。实际的替换类包含在运行时库中，以便在您执行修改过的代码时它们是可用的。</P>
<P>按照标准开发周期来说，字节码增强器需要在 Java 代码编译之后、类文件为部署而打包之前运行。在您使用一个 IDE 时，该更改是一个问题 ——“集成”一个类转换工具到“开发环境”是很痛苦的事情，因为 IDE 一般假设它们拥有类文件。限制这一痛苦的一种方式是，只对 IDE 中的大多数测试使用 JDK 5.0。这样，您只需要在想要为部署打包文件或者想要测试实际的部署 JVM 时转换类文件。如果使用 Ant 风格的构建过程，就没有问题；只添加 Retroweaver 字节码增强器作为编译之后的一个步骤。</P>
<P>Retroweaver 具有一个小小的限制：尽管 Retroweaver 允许您在运行在旧版 JVM 上的代码中使用 J2SE 5.0 语言特性，但是它并不支持也包含在 J2SE 5.0 中的所有添加到标准 Java 类的特性。如果您的代码使用任何添加到 J2SE 5.0 中的类或方法，那么就将在试图加载旧版 JVM 中的代码时得到错误，哪怕是在 Retroweaver 处理完成之后也如此。避免对标准库的 J2SE 5.0 添加不应该是一个主要问题，但是如果使用 IDE 中的感应弹出特性并偶然挑选了一个仅添加到 J2SE 5.0 中的方法或类，它就有可能让您得到错误。</P>
<P><A name=h1.1><SPAN class=atitle3>它做什么</SPAN></A><BR>J2SE 5.0 的更改既发生在 JVM 中，也发生在实际的 Java 语言，但是 JVM 更改相当小。有一个新的字符可以用于字节码中的标识符中 ("+")，一些处理类引用的指令发生了修改，还有一个不同的方法用于处理合成组件。 Retroweaver 在字节码增强步骤中处理这些 JVM 更改，方法是把这些更改返回原样，即替换成用于 J2SE 5.0 之前相同目的的方法（比如标识符中的 + 字符，就是用 $ 取代它）。</P>
<P>包含在 J2SE 5.0 中的语言更改要稍微复杂一点。一些最有趣的更改，比如增强的 <CODE>for</CODE> 循环，基本上只是语法更改，即为表示编程操作提供快捷方式。比如泛型更改 —— 泛型类型信息 —— 由编译器用于实施编译时安全，但是生成的字节码仍然到处使用强制转换。但是大多数更改使用了添加到核心 Java API 中的类或方法，所以您不能直接使用为 JDK 5.0 生成的字节码并将它直接运行在早期的 JVM 上。Retroweaver 为支持 J2SE 5.0 语言更改所需的新类提供其自己的等价物，并且用对其自己的类的引用替换对标准类的引用，这是字节码增强步骤的一部分。</P>
<P>Retroweaver 字节码增强不能对所有的 J2SE 5.0 语言更改提供完全支持。例如，没有对处理注释的运行时支持，因为运行时支持涉及到对基本 JVM 类加载器实现的更改。但是一般来说，只是不支持那些不会影响普通用户的小特性。</P>
<P><A name=h1.2><SPAN class=atitle3>Retroweaver 发挥作用</SPAN></A><BR>使用 Retroweaver 简直是太容易了。可以使用一个简单的 GUI 界面或者控制台应用程序来在应用程序类文件上运行字节码增强。两种方式都只要在将要转换的类文件树的根目录指出 Retroweaver 即可。在运行时，如果使用任何需要运行时支持的特性（比如 enums），那么就需要在类路径中包含 Retroweaver 运行时 jar。</P>
<P>清单 1 给出了一个简单的示例程序，其中使用了一些 J2SE 5.0 特性。<CODE>com.sosnoski.dwct.Primitive</CODE> 是一个针对 Java 语言原语类型的 <CODE>enum</CODE> 类。<CODE>main()</CODE> 方法使用增强的 <CODE>for</CODE> 循环来迭代通过不同的原语，并在当前实例上使用一个简单的 <CODE>switch</CODE> 语句来设置每个原语的大小值。</P><A name=code1><B>清单 1. 简单的 J2SE 5.0 enum 示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sosnoski.dwct;

public enum Primitive
{
    BOOLEAN, BYTE, CHARACTER, DOUBLE, FLOAT, INT, LONG, SHORT;
    
    public static void main(String[] args) {
        for (Primitive p : Primitive.values()) {
            int size = -1;
            switch (p) {
                case BOOLEAN:
                case BYTE:
                    size = 1;
                    break;
                case CHARACTER:
                case SHORT:
                    size = 2;
                    break;
                case FLOAT:
                case INT:
                    size = 4;
                    break;
                case DOUBLE:
                case LONG:
                    size = 8;
                    break;
            }
            System.out.println(p + " is size " + size);
        }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>使用 JDK 5.0 编译并运行清单 1 代码会给出清单 2 中的输出。但是不能在早期的 JDK 下编译或运行清单 1 代码；由于特定于 J2SE 5.0 的特性会导致编译失败，而运行失败会抛出 <CODE>java.lang.UnsupportedClassVersionError</CODE> 异常。</P><A name=code2><B>清单 2. enum 示例输出</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
[dennis@notebook code]$ java -cp classes com.sosnoski.dwct.Primitive
BOOLEAN is size 1
BYTE is size 1
CHARACTER is size 2
DOUBLE is size 8
FLOAT is size 4
INT is size 4
LONG is size 8
SHORT is size 2
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 3 展示了在 <CODE>Primitive</CODE> 类上运行 Retroweaver。这个类实际上编译为两个类文件，一个用于 enum 类，另一个支持在 <CODE>switch</CODE> 语句中使用 enum。（注意，清单代码换行是为了适应页面宽度。）</P><A name=code3><B>清单 3. enum 示例输出</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
[dennis@notebook code]$ java -cp retro/release/retroweaver.jar:retro/lib/bcel-5.1.jar:retro/lib/
  jace.jar:retro/lib/Regex.jar com.rc.retroweaver.Weaver -source classes
[RetroWeaver] Weaving /home/dennis/writing/articles/devworks/series/may05/code/
  classes/com/sosnoski/dwct/Primitive$1.class
[RetroWeaver] Weaving /home/dennis/writing/articles/devworks/series/may05/code/
  classes/com/sosnoski/dwct/Primitive.class
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在运行 Retroweaver 之后，这些类就可以用于 JDK 5.0 和 JDK 1.4 JVM 上了。当使用 1.4 JVM 运行修改后的类时，输出与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#code2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 2</A> 中的相同。Retroweaver 提供命令行选项来指定旧的 1.3 和 1.2 JVM 以取代默认的 1.4 目标，但是我下载的运行时 jar 版本需要 1.4，我不想重新构建它以检查对早期 JVM 的支持。</P>
<P><A name=h2><SPAN class=atitle2>JDK 1.4 上的注释</SPAN></A><BR>既然已经看到了 Retroweaver 如何让您运行在早期 JVM 上的同时在源代码中使用 J2SE 5.0 特性，我将返回到 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 的代码。以防您没有阅读上一期，我在此做一个总结：我展示了如何使用 ASM 2.0 基于注释实现运行时类转换，并给出一个注释的特定例子，该注释用于指定 <CODE>toString()</CODE> 方法中应该包括哪些字段。</P>
<P>上个月的代码只适用于 JDK 5.0 或更高版本。在本文中，我将修改代码以适用于早期 JVM。与 Retroweaver 一起使用，自动化 <CODE>toString()</CODE> 生成的好处将会扩展到许多还停留在 J2SE 5.0 之前运行时的 Java 开发人员。</P>
<P><A name=h2.1><SPAN class=atitle3>回忆 ToStringAgent</SPAN></A><BR>我用于对 JDK 5.0 实现 <CODE>toString()</CODE> 方法生成的 <CODE>com.sosnoski.asm.ToStringAgent</CODE> 类对于旧版 JVM 有一个小小的问题：它使用 J2SE 5.0 中新增的 instrumentation API 来在运行时截取类加载和修改类。在早期 JVM 中截取类加载不太灵活，但是并不是不可能 —— 只需要用您自己的版本来取代用于应用程序的类加载器就可以了。由于所有的应用程序类都是通过您的自定义类加载器加载的，所以在它们被实际提供给 JVM 之前，您可以自由地修改类表示。</P><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">在上一篇文章中，我使用这种代入自定义类加载器的技术来在运行时修改类（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#resources">参考资料</A>）。这里我不想重复背景材料，但是如果您感兴趣的话，可参阅上一篇文章。</I> 
<P>更新 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 的代码以使用自定义类加载器方法是很容易的。清单 4 展示了带有所有修改的类。该类取代了上一期文章中使用的 <CODE>com.sosnoski.asm.ToStringAgent</CODE> 类。上一期中使用到的其他类保持不变。</P><A name=code4><B>清单 4. ToStringLoader 代码</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sosnoski.asm;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

public class ToStringLoader extends URLClassLoader
{
    private ToStringLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    // override of ClassLoader method
    protected Class findClass(String name) throws ClassNotFoundException {
        String resname = name.replace('.', '/') + ".class";
        InputStream is = getResourceAsStream(resname);
        if (is == null) {
            System.err.println("Unable to load class " + name +
                " for annotation checking");
            return super.findClass(name);
        } else {
            System.out.println("Processing class " + name);
            try {
                
                // read the entire content into byte array
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] buff = new byte[1024];
                int length;
                while ((length = is.read(buff)) &gt;= 0) {
                    bos.write(buff, 0, length);
                }
                byte[] bytes = bos.toByteArray();
                
                // scan class binary format to find fields for toString() method
                ClassReader creader = new ClassReader(bytes);
                FieldCollector visitor = new FieldCollector();
                creader.accept(visitor, true);
                FieldInfo[] fields = visitor.getFields();
                if (fields.length &gt; 0) {
                    
                    // annotated fields present, generate the toString() method
                    System.out.println("Modifying " + name);
                    ClassWriter writer = new ClassWriter(false);
                    ToStringGenerator gen = new ToStringGenerator(writer,
                            name.replace('.', '/'), fields);
                    creader.accept(gen, false);
                    bytes = writer.toByteArray();
                }
                
                // return the (possibly modified) class
                return defineClass(bytes, 0, bytes.length);
                
            } catch (IOException e) {
                throw new ClassNotFoundException("Error reading class " + name);
            }
        }
    }

    public static void main(String[] args) {
        if (args.length &gt;= 1) {
            try {
                
                // get paths to be used for loading
                ClassLoader base = ClassLoader.getSystemClassLoader();
                URL[] urls;
                if (base instanceof URLClassLoader) {
                    urls = ((URLClassLoader)base).getURLs();
                } else {
                    urls = new URL[] { new File(".").toURI().toURL() };
                }
                
                // load the target class using custom class loader
                ToStringLoader loader =
                    new ToStringLoader(urls, base.getParent());
                Class clas = loader.loadClass(args[0]);
                    
                // invoke the "main" method of the application class
                Class[] ptypes = new Class[] { args.getClass() };
                Method main = clas.getDeclaredMethod("main", ptypes);
                String[] pargs = new String[args.length-1];
                System.arraycopy(args, 1, pargs, 0, pargs.length);
                Thread.currentThread().setContextClassLoader(loader);
                main.invoke(null, new Object[] { pargs });
                
            } catch (Exception e) {
                e.printStackTrace();
            }
            
        } else {
            System.out.println("Usage: com.sosnoski.asm.ToStringLoader " +
                "report-class main-class args...");
        }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>为了使用清单 4 代码，我仍然需要使用 JDK 5.0 编译与注释相关的代码，然后在产生的类集合上运行 Retroweaver。我也需要在类路径中包含 retroweaver.jar 运行时代码（因为 Retroweaver 对已转换的注释使用它自己的类）。清单 5 展示了运行与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 相同的测试代码所产生的输出，但是这一次使用了 Retroweaver 和清单 4 中的 <CODE>ToStringLoader</CODE> 类，其中命令行换行是为了适应页面宽度）。</P><A name=code5><B>清单 5. JDK 1.4 上的 ToString 注释</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
[dennis@notebook code]$ java -cp classes:retro/release/retroweaver-rt.jar:lib/
  asm-2.0.RC1.jar:lib/asm-commons-2.0.RC1.jar
  com.sosnoski.asm.ToStringLoader com.sosnoski.dwct.Run
Processing class com.sosnoski.dwct.Run
Processing class com.sosnoski.dwct.Name
Modifying com.sosnoski.dwct.Name
Processing class com.sosnoski.dwct.Address
Modifying com.sosnoski.dwct.Address
Processing class com.sosnoski.dwct.Customer
Modifying com.sosnoski.dwct.Customer
Customer: #=12345
 Name: Dennis Michael Sosnoski
 Address: street=1234 5th St. city=Redmond state=WA zip=98052
 homePhone=425 555-1212 dayPhone=425 555-1213
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 5 显示了生成的 <CODE>toString()</CODE> 方法的输出，其末尾部分与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 代码的 JDK 5.0 版本的结果相同。被处理的类列表几乎是相同的，只是用于截取类加载的技术不同。用于 JDK 1.4 的自定义类加载器方法不提供 JDK 5.0 instrumentation API 的完全灵活性，但是它适用于所有最近的 JVM，并允许您修改任何应用程序类。</P>
<P><A name=h3><SPAN class=atitle2>结束语</SPAN></A><BR>在本期文章中，我展示了如何使用 Retroweaver 来使 J2SE 5.0 Java 代码可运行在旧版 JVM 上。如果您喜欢新的 J2SE 5.0 语言特性，并迫不及待想在自己的应用程序中使用这些特性，那么 Retroweaver 提供了完美的解决方案：您可以马上在开发中开始使用这些语言特性，根本不会影响生产平台。作为 Retroweaver 发挥作用的一个例子，我也 backport 了 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 的基于注释的 <CODE>ToString</CODE> 生成器，以在早期 JVM 上运行。</P>
<P>对于下个月的文章，我将回到在上一期文章中简要提到的一个问题，即注释与外部配置文件之间的权衡。在配置文件疯狂了很多年之后，整个的 Java 扩展集合似乎都一股脑儿转向使用注释了。但是难道注释总是提供配置类型信息的最佳方式吗？我对此表示怀疑，下个月我将提供一些例子，以及一些我个人的最佳实践指导方针。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/java/library/j-cwt07065/" target=_blank xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">英文原文</A>。<BR><BR>
<LI>单击本文顶部或底部的 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">代码</B> 图标，下载文中讨论的源代码。<BR><BR>
<LI>想要开始在旧版 JVM 上使用 J2SE 5.0 语言特性？请直接查看 <A href="http://retroweaver.sf.net/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Retroweaver 项目</A> 的开放源代码。<BR><BR>
<LI>获得 <A href="http://asm.objectweb.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">ASM</A> 这个快速而灵活的 Java 字节码操作框架的所有详细资料。<BR><BR>
<LI>对 J2SE 5.0 与旧版 Java 平台的区别感兴趣？请查看 John Zukowski 撰写的 <A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=%E9%A9%AF%E6%9C%8D+Tiger%3A" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>驯服 Tiger</I></A> 系列，了解所有的更改。<BR><BR>
<LI>在 <A href="http://www.jcp.org/en/jsr/detail?id=175" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JSR-175: A Metadata Facility for the Java Programming Language</A> 中找到关于 J2SE 注释的所有信息。<BR><BR>
<LI>关于使用自定义类加载器在运行时进行类转换的深入讨论，请参阅作者的文章“<A href="http://www-128.ibm.com/developerworks/cn/java/j-dyn0203/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java 编程的动态性，第 5 部分: 动态转换类</A>”（developerWorks, 2004 年 2 月）。<BR><BR>
<LI>不要错过 Dennis Sosnoski 撰写的 <A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=Classworking+%E5%B7%A5%E5%85%B7%E7%AE%B1%3A+" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>Classworking 工具箱</I></A> 系列中的其他文章。<BR><BR>
<LI>请参阅 Peter Haggar 撰写的“<A href="http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java bytecode: Understanding bytecode makes you a better programmer</A>”（developerWorks, 2001 年 7 月），了解关于 Java 字节码设计的更多信息。<BR><BR>
<LI>同样由 Dennis Sosnoski 撰写的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-dyn0429/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>Java 编程的动态性</I></A> 系列，将带您漫游 Java 类结构、发射和 classworking。<BR><BR>
<LI><A href="http://jikes.sourceforge.net/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Jikes</A> 开放源代码项目提供了 Java 编程语言的非常快速和高兼容性的编译器。可以使用它来老式地生成字节码 —— 从 Java 源代码生成。<BR><BR>
<LI>要了解更多关于 Java 技术的信息，请访问 <A href="http://www.ibm.com/developerworks/cn/java/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks Java 专区</A>。您将找到技术文档、how-to 文章、教程、下载、产品信息，以及更多内容。 <BR><BR>
<LI>请访问 <A href="http://www.ibm.com/developerworks/cn/java/newto/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">New to Java technology</A> 站点，找到帮助您开始 Java 编程的最新资源。<BR><BR>
<LI>通过参与 <A href="http://www.ibm.com/developerworks/blogs/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks blogs</A> 加入 developerWorks 社区。<BR></LI></UL><img src ="http://www.blogjava.net/mstar/aggbug/8590.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-28 08:53 <a href="http://www.blogjava.net/mstar/archive/2005/07/28/8590.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Prepare for New EJB 3.0 Persistence</title><link>http://www.blogjava.net/mstar/archive/2005/07/28/8589.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 28 Jul 2005 00:46:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/28/8589.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/8589.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/28/8589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/8589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/8589.html</trackback:ping><description><![CDATA[<P class=FeatureHead><A href="http://www.ftponline.com/channels/java/2005_06/mschincariol_06_22_05/">http://www.ftponline.com/channels/java/2005_06/mschincariol_06_22_05/</A><BR>Prepare for New EJB 3.0 Persistence<BR><SPAN class=FeatureDek>See how to address the migration concerns of enterprise application developers and architects who are currently using EJB 2.1 CMP</SPAN><BR><SPAN class=AboutAuthor><EM>by Merrick Schincariol and Doug Clarke</EM></SPAN> </P><!-- posted date in this format: Posted September 17, 2003 -->
<P><EM>June 22, 2005</EM></P><!-- end posted date -->
<P><SPAN class=DropCap>M</SPAN>uch has already been much written and discussed concerning the upcoming changes to Enterprise JavaBeans (EJB) with the proposed 3.0 specification. The simplifications of the component model and metadata configuration will definitely make this update an important evolution of J2EE. One of the most significant changes within EJB 3.0 is the new persistence model. This new persistence API and approach to modeling is based on the success of leading object-relational mapping products that use a Plain Old Java Object (POJO) approach to persistence.</P><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad -->
<P>We'll look into EJB 3.0 persistence and address the concerns of enterprise application developers and architects who are currently using EJB 2.1 CMP. The process of moving from EJB 2.1 persistence to EJB 3.0 can range from coexistence to a full migration and adoption of the new model. Although much has already been written about the new persistence model, specific strategies and development and design patterns have not been detailed. We'll attempt to fill these gaps with concrete solution options while visiting the key benefits that application developers will gain through this migration. We assume that you are familiar with the basic elements of the new persistence API. (Note: Look for an upcoming article on migrating J2EE applications to EJB 3.0 in <EM>Java Pro</EM> magazine by Debu Panda, a member of the Oracle Application Server Development team, that will post on FTPOnline in July, 2005.) </P>
<P><SPAN class=subhead>Why Migrate?</SPAN><BR>One question participants in J2EE projects should discuss when planning an upgrade to an EJB 3.0-enabled J2EE container is: Why should we migrate our application? These new EJB 3.0 containers will be backward compatible and thus support their existing application without modification. This backward compatibility means that upgrading a container to EJB 3.0 does not mean in itself that an application needs to be rewritten. However, what about this new persistence API? </P><SPAN id=lblShortArticlePage>
<P>The real benefits in migrating from an EJB 2 persistence solution using container-managed persistence (CMP) include reduction in the number of artifacts and complexity surrounding beans, interfaces, and home interfaces that are no longer required in the 3.0 approach; the ability to test entities and the application code that uses them outside of the container—the EJB 2.1 persistence solutions have led most applications to avoid strong unit testing of these components; and reduction in unnecessary code that works around the limitations of EJB 2.1 entity beans. These patterns include data-transfer objects (DTOs) and service locator implementations. </P>
<P>These benefits are significant to the developer productivity and overall application quality that comes with improved testing. The challenge is that if you are to see these benefits, the application’s persistence model and the client code that uses the model must be migrated. Unlike the session- and message-driven bean components, the entity bean migration is potentially much more intrusive and should be undertaken with a better understanding of the associated costs. </P>
<P>Migrating from EJB 2.1 to EJB 3.0 CMP involves migrating the model and its mappings, the queries, and the applications’ client code to the persistence layer. The migration can be divided into three basic approaches: 1) migrate the entity DTOs to EJB 3.0 entities; migrate the EJB 2.1 entity beans to EJB 3.0 entities; and 3) create a new EJB 3.0 entity model. <BR></P>
<P>The first two approaches are intended to minimize code changes in your application. The third involves creating a new model, possibly using forward-generation tools to reduce coding and configuration work. Although this may be the most efficient way to create the EJB 3.0 persistence model, it will most likely incur the most migration costs within the application code. </P>
<P><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --></P>
<P>Before we discuss which approach is best for a given application, the issue of DTOs and their role in EJB 3.0 persistence should be clarified. In EJB 2.1, DTOs were required for getting persistent data outside of the container for use in another tier of the application. In EJB 3.0, entities can be serialized and the need to use DTOs is removed. This factor doesn't mean that DTOs cannot be used if they provide necessary decoupling, but it does address the common practice of having DTOs that mirror entity beans and replicate business logic. These will be referred to as "entity DTOs," and they are no longer required. </P>
<P>Another flavor of DTO is the "view DTO," which is an object that does not mirror the CMP entities but instead provides a more coarse-grained view of data that may come from one or more entity beans. View DTOs are used to optimize interaction between tiers. EJB 3.0 does not remove the need for this style of DTO but instead provides facilities to simplify their use. Whereas in traditional applications, the underlying entities are retrieved and populated programmatically into view DTOs, EJB 3.0 query results may now be directly projected onto view DTOs without any additional binding code. </P>
<P><SPAN class=subhead>Nonpersistent Classes</SPAN><BR>In EJB 3.0 an EJB QL query can return results by using nonpersistent classes. In this case, the query statement defines the attributes needed and the selection criteria relative to the persistence model, but instead of returning one of these types, a nonpersistent class is specified. The NEW operator is used for this specification and defines the class and constructor to be used when building these result objects.</P><PRE class=codesnippet>SELECT NEW example.EmpView(
  e.id, e.firstName, e.lastName, 
  d.name)
FROM Employee e JOIN 
  e.department d 
WHERE e.salary &gt; 50000</PRE>
<P>Deciding to migrate your entity DTOs to become your EJB 3.0 entities is a good choice for minimizing changes on the client tier that use these serialized objects. It is also a good choice if your application logic in the EJB tier is minimal or also focused on the entity DTOs. </P>
<P>This approach requires that the entity DTO is a valid JavaBean and maintains a one-to-one correspondence with the data elements in the associated entity bean. It is also highly recommended that a DAO layer be in place, so that the persistent logic can be updated in isolation once the DTOs have been converted. If your application logic makes direct use of EJB 2.1 entities, the techniques described for migrating entity beans may be more suitable. </P>
<P>The first step in the migration process is to mark the entity DTO as a persistent object by adding the @Entity annotation to the class definition. Without any further changes, the entity DTO is now a persistent object that maps to the database, using the EJB 3.0 defaults. The table name is assumed to be the class name, and each public JavaBean getter method is assumed to correspond to a column in the table with the same name as the property. You can customize these default mappings by using @Table, @Column, and other EJB 3.0 annotations. </P>
<P>After each entity DTO is marked as a persistent object and the database mappings are set up correctly, the next step is to define the relationships between the new entities. EJB 3.0 includes a full set of object-relational annotations to identify entity relationships and control their behavior during persistent operations. The &lt;ejb-relation&gt; descriptor in the EJB 2.1 ejb-jar.xml file can be directly mapped onto each pair of entities involved in the relationship. Consider the relationship descriptor shown in <A href="javascript:openWindow1();"><FONT color=#002c99>Listing 1</FONT></A>. This fragment from an Employee entity demonstrates how the Emps-have-Dept role would be mapped: </P><PRE class=codesnippet>@Entity
public class Employee {
  private Department department;

  // ...

  @ManyToOne
  public Department 
    getDepartment() {
      return department;
  }

  public void setDepartment(
    Department dept) {
      this. department = dept;
  }
}<BR><P>Much as how the table and field names are assumed by default and can be overridden, so can the foreign key column names. For these the relationship mapping annotations make use of @JoinColumn to provide column names that are different from the defaults. </P><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --><P><BR>It is important to note that at this point, there are no behavioral changes to the existing application. We have merely added metadata to the original entity DTOs that makes it possible to use them as persistent EJB 3.0 entities. Assuming there is a DAO layer in place that manages the entity DTOs, this layer can now be updated to use the EntityManager to retrieve, store, and update the newly annotated persistent objects. </P><P><SPAN class=subhead>Migrate EJB 2.1 Entity Beans</SPAN><BR>The other alternative for migrating to the new EJB 3.0 persistence model is to leverage the existing EJB 2.1 entity model. For the purposes of this discussion, we'll assume that only local interfaces to the entity beans are used because these are the most prevalent in today's applications and because entities as remote objects are not supported in EJB 3.0. For those applications using entity beans as remote objects, the migration will involve more-fundamental design changes.</P><P>The goal in this type of migration is to minimize the changes to client code, which is bound to the local and home interfaces of an entity: </P><OL><LI>Remove the abstract designation on the bean class, and provide concrete implementations of the property accessor (get/set) methods. <LI>Annotate the bean class with the object-relational annotations that map the entity to the database, and describe its relationships. <LI>Rename the bean class to be that of the local interface, and remove the local interface to minimize changes to application code that already uses the bean through this interface. <LI>Convert finders and selects associated with the bean to @NamedQuery annotations on the bean class. </LI></OL><P>If your entity bean acts as a client to other beans, this code must be migrated as application client code, as discussed later. Consider this EJB 2.1 Department bean: </P><PRE class=codesnippet>public abstract class 
  DepartmentBean implements 
  EntityBean {
  public abstract Long getId();
  public abstract void setId(
    Long id);
  public abstract String 
    getName();
  public abstract void setName(
    String name);
  public abstract Collection 
    getEmployees();
  public abstract void 
    setEmployees(
    Collection employees);

  public void ejbCreate(Long id) 
    throws CreateException {}
  public void ejbPostCreate(
    Long id) throws 
    CreateException {}
  public void ejbStore() {}
  public void ejbLoad() {}
  public void ejbRemove() {}
  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void setEntityContext(
    EntityContext ctx) {}
  public void unsetEntityContext() 
    {}
}</PRE><BR><P>It contains the following finder definitions: </P><PRE class=codesnippet>&lt;query&amp;th;
  &lt;description&gt;&lt;/description&gt;
  &lt;query-method&gt;
    &lt;method-name&gt;findAll
    &lt;/method-name&gt;
    &lt;method-params/&gt;
  &lt;/query-method&gt;
  &lt;result-type-mapping&gt;Local
  &lt;/result-type-mapping&gt;
  &lt;ejb-ql&gt;Select OBJECT(d) From 
    Department d&lt;/ejb-ql&gt;
&lt;/query&gt;
&lt;query&gt;
  &lt;description&gt;&lt;/description&gt;
  &lt;query-method&gt;
    &lt;method-name&gt;findByDeptName
    &lt;/method-name&gt;
    &lt;method-params&gt;
      &lt;method-param&gt;
        java.lang.String
      &lt;/method-param&gt;
      &lt;/method-params&gt;
  &lt;/query-method&gt;
  &lt;result-type-mapping&gt;Local
  &lt;/result-type-mapping&gt;
  &lt;ejb-ql&gt;SELECT DISTINCT 
    OBJECT(d) FROM Department d 
    WHERE d.name = ?1&lt;/ejb-ql&gt;
&lt;/query&gt;</PRE><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --><P><BR>By following the steps listed previously, we end up with the code you see in <A href="javascript:openWindow2();"><FONT color=#002c99>Listing 2</FONT></A>. </P><P>Of special interest is the use of EJB 2.1 container-managed relationships (CMR). In EJB 3.0, relationship management is performed with standard Java coding practices. It is the responsibility of the developer to ensure that both sides of a relationship are updated correctly when relationships are formed. Generally speaking, one additional line of code is required for every relationship operation to ensure that the model is consistent. With respect to collections, we recommend the use of helper methods such as add and remove instead of direct manipulation of the collection in client code: </P><PRE class=codesnippet>@Entity
public class Department {
  private Collection&lt;Employee&gt; employees;

  // ...

  public void addEmployee(
    Employee emp) {
    employees.add(emp);
    emp.setDepartment(this);
  }
}</PRE><P>Besides these more common migration changes, there is additional EJB 2.1 entity bean implementation that may require work. These include the use of the EntityContext within the bean itself and ejbHome methods. An EJB 3.0 entity will not have an EntityContext. The entity can simply use "this" to resolve to itself and pass itself into other beans’ methods. Any ejbHome methods implemented can simply remain as is or be converted into static methods to make their use more obvious. </P><P><SPAN class=subhead>Migrate EJB 2.1 Entity Homes</SPAN><BR>The home interface is the primary mechanism that enables application code to locate and manage entity beans. Because EJB 3.0 has nothing equivalent to home interfaces, it also presents the greatest challenge in migration.</P><P>To minimize changes to client code, the easiest approach to migrating homes is to create a helper class that holds onto an EntityManager and implement the methods called on the home by the application code. The example shown in <A href="javascript:openWindow3();"><FONT color=#002c99>Listing 3</FONT></A> demonstrates a helper class that functions as a replacement home. </P><P>This helper class leverages the named queries we defined on the entity class to implement finders, although dynamic queries could also have been used. The remove method on this helper replaces the EJBLocalObject.remove() previously on the entity bean. It is important that all calls to the bean’s remove method be migrated to call this one provided on the helper or to directly make the call with the EntityManager. </P><P>The application code that makes use of EJB 2.1 entity beans is coupled tightly to the Home and Local or Remote interfaces of the entity beans. The migration of this code is least likely to be automated; however: </P><UL><LI>Home interface lookup and query execution must be modified to either directly use the EntityManager or a Home-helper class that is not managed by the container. <LI>Code using the entity beans interfaces must be changed to use the new EJB 3.0 entity. The entity class can be the interface, the entity DTO, or some new entity bean class. The selection and intrusiveness of these changes will depend on the chosen model-migration approach. <LI>Entity DTO detachment and attachment code can be removed, in favor of detachment/attachment of the entities. <LI>View DTO creation code can be replaced with EJB QL queries that dynamically create the nonpersistent view objects. </LI></UL><P>Although it is not possible to dive into all of the details of a migration from EJB 2.1 CMP to the new EJB 3.0 persistence model, we have addressed the major issues. Understanding the differences between the models and migration options available is key to planning such an effort. As the persistence vendors continue to make early previews of the technology available, more detailed technical information and tooling to assist in this migration will become available. </P><P>Those wanting to try these migration steps today require an EJB 3.0 preview and an IDE with good refactoring and J2SE 5.0 support. Oracle's EJB 3.0 preview and technical information are available at <A href="http://otn.oracle.com/ejb3" target=_blank><FONT color=#002c99>http://otn.oracle.com/ejb3</FONT></A>. For those wanting to get more involved in the EJB 3.0 persistence specification, the drafts are available at <A href="http://java.sun.com/products/ejb" target=_blank><FONT color=#002c99>http://java.sun.com/products/ejb</FONT></A>, and you can provide feedback at <A href="mailto:ejb3-feedback@sun.com"><FONT color=#002c99>ejb3-feedback@sun.com</FONT></A>. </P><P><SPAN class=AboutAuthor>About the Authors</SPAN><BR>Merrick Schincariol is a senior engineer for Oracle Application Server and one of the lead developers of the Oracle Application Server EJB 3.0 Preview. Doug Clarke is a senior principal product manager for the Oracle TopLink product, with extensive development, consulting, and educational field experience focusing on persistence. Contact Merrick at <A href="mailto:merrick.schincariol@oracle.com"><FONT color=#002c99>merrick.schincariol@oracle.com</FONT></A>, and contact Doug at <A href="mailto:douglas.clarke@oracle.com"><FONT color=#002c99>douglas.clarke@oracle.com</FONT></A>. </P></PRE></SPAN><!-- End Content Here --><!-- If there is only 1 page, delete the function --><img src ="http://www.blogjava.net/mstar/aggbug/8589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-28 08:46 <a href="http://www.blogjava.net/mstar/archive/2005/07/28/8589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Migrate J2EE Applications for EJB 3.0</title><link>http://www.blogjava.net/mstar/archive/2005/07/28/8588.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 28 Jul 2005 00:21:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/28/8588.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/8588.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/28/8588.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/8588.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/8588.html</trackback:ping><description><![CDATA[<P class=FeatureHead><A href="http://www.ftponline.com/javapro/2005_07/magazine/features/dpanda/Default.aspx">http://www.ftponline.com/javapro/2005_07/magazine/features/dpanda/Default.aspx</A><BR>Migrate J2EE Applications for EJB 3.0<BR><SPAN class=FeatureDek>Avoid inherent complexities when migrating applications to use the new standard in server-side business logic programming </SPAN><BR><SPAN class=AboutAuthor><EM>by Debu Panda </EM></SPAN></P><!-- posted date in this format: Posted September 17, 2003 -->
<P><EM>July 21, 2005</EM></P><!-- end posted date -->
<P><SPAN class=DropCap>T</SPAN>he programming model for Enterprise JavaBeans (EJBs) has been simplified dramatically in EJB 3.0 and is being hailed by Java developers as the new standard of server-side business logic programming. Meanwhile, the existence of thousands of J2EE applications written with earlier versions of the EJB API has raised concerns about both the interoperability of EJB 3.0 with these applications and migrating the applications to use EJB 3.0. </P><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad -->
<P>Major application server vendors that already provide EJB 3.0 features will continue to support EJB 2.x in the new EJB 3.0 container. This support means that applications written with EJB 2.x will continue to run without any change whatsoever. However, some organizations will certainly want to migrate their applications to use the EJB 3.0 API to improve maintainability by simplifying their code and take advantage of the performance and scalability benefits of the persistence API. </P>
<P>Let's look at some of the issues relevant to migrating applications to EJB 3.0. The topics discussed here compose a subset of all possible issues a migration effort will encounter, and because the spec is not finalized other issues may still arise. </P>
<P>The main goals of EJB 3.0 are to simplify the programming model and to define a persistence API for the Java platform. Some general changes to the EJB spec that will simplify life for EJB developers are: EJB artifacts are Plain Old Java Objects (POJOs), XML descriptors are no longer necessary, annotations may be used instead, defaults are assumed whenever possible, unnecessary artifacts and life-cycle methods are optional, and the client view is simplified by dependency injection. </P>
<P>Standardization of the POJO persistence model (such as the one used by Oracle TopLink and JBoss Hibernate) within the context of J2EE finally provides users with the inheritance and polymorphism that have until now been available only outside the container or in proprietary products. Furthermore, the POJO entity beans can now be used and tested both inside and outside the EJB container. </P>
<P><SPAN class=subhead>Migrate Session Beans</SPAN><BR>The main changes in EJB 3.0 session beans simplify development by making beans POJOs that use annotations instead of XML descriptors and dependency injection instead of complex JNDI lookup. Hence, it is very easy to migrate the session beans to use the EJB 3.0 programming model. The changes in EJB 3.0 sessions fall into two categories: the server side and the client side. By client side we mean using resources, EJBs, and so on from other EJBs. Using EJBs from other types of clients outside the EJB container will be standardized with the J2EE 5.0 specification.</P>
<P>Some application servers allow migration of server-side components without affecting the clients, thus allowing incremental migration of applications. </P>
<P>There are some changes that need to be made to EJB 2.x session beans to migrate to EJB 3.0. In EJB 3.0 the remote and local interfaces do not have to implement javax.ejb.EJBObject or javax.ejb.EJBLocalObject. The component interface can become a business interface, and @Remote annotations can be used to mark remote interfaces. </P><SPAN id=lblShortArticlePage>
<P>Let's look at two examples of the same EJB 2.x interface, before and after migration to EJB 3.0. RemoteExceptions are no longer thrown by the methods on the EJB 2.x remote interface: </P><PRE class=codesnippet>import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface HelloWorld 
	extends EJBObject {
	void sayHello(String name) 
		throws RemoteException;
}

and the EJB 3.0 remote interface:

import javax.ejb.Remote;

@Remote
public interface HelloWorld {
	void sayHello(String name);
}<BR><P>Bean classes do not need to implement javax.ejb.SessionBean—rather, their business interfaces. Life-cycle methods that are not required do not need to be present on the bean class, and callbacks may be indicated by annotations. Here is the EJB 2.x stateless session bean: </P><PRE class=codesnippet>import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

public class HelloWorldBean 
	implements SessionBean {

	public void ejbCreate() {}
	public void ejbActivate() {}
	public void ejbPassivate() {}
	public void ejbRemove() {}
	public void setSessionContext(
		SessionContext ctx) {}

	public void sayHello(
		String name) {
	System.out.println(
		"Hello " + name);
	}
}</PRE><P>and here is the stateless session bean migrated to EJB 3.0: </P><PRE class=codesnippet>import javax.ejb.Stateless;

@Stateless
public class HelloWorldBean 
	implements HelloWorld {
	public void sayHello(
		String name)  {
		System.out.println(
			"Hello " + name);
	}
}</PRE><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --><P><BR>Stateful session bean ejbCreate() methods become business methods used at initialization time. Removal methods are annotated with @Remove. Optionally, you can migrate session bean code to use annotations for transactions and security. </P><P>EJB 3.0 simplifies the use of resources and EJB references by making use of the principle of dependency injection. Injection of resources or references can occur through either annotation of the injection target or specification of the target in the ejb-jar.xml descriptor. <A href="javascript:openWindow1();"><FONT color=#002c99>Table 1</FONT></A> lists some of the differences. <BR><TABLE style="BORDER-RIGHT: #666666 1px solid; BORDER-TOP: #666666 1px solid; BORDER-LEFT: #666666 1px solid; BORDER-BOTTOM: #666666 1px solid" cellSpacing=2 cellPadding=4 width="100%" border=0><TBODY><TR bgColor=#333399><TH class=tabletext style="COLOR: #ffffff" vAlign=top align=left></TH><TH class=tabletext style="COLOR: #ffffff" vAlign=top align=left>EJB 2.x</TH><TH class=tabletext style="COLOR: #ffffff" vAlign=top align=left>EJB 3.0</TH></TR><!-- Start Content of Table --><TR bgColor=#cccccc><TD class=tabletext vAlign=top><STRONG>Using another EJB:</STRONG></TD><TD class=tabletext vAlign=top>ejb-ref in XML descriptor Do JNDI lookup to get the home interface <BR><BR>Call home.create to obtain instance </TD><TD class=tabletext vAlign=top>ejb-ref no longer requires the home interface Change the home interface to the business interface<BR><BR>Remove home.create and directly invoke the methods on the EJB <BR><BR>Optionally annotate the EJB business interface property/field to obtain an instance </TD></TR><TR bgColor=#ffffcc><TD class=tabletext vAlign=top><STRONG>Resource access:</STRONG></TD><TD class=tabletext vAlign=top>Do JNDI lookup to obtain a resource</TD><TD class=tabletext vAlign=top>Optionally annotate a resource property/field to obtain a resource</TD></TR><!-- End Content of Table --></TBODY></TABLE><TABLE cellSpacing=0 cellPadding=0 width="100%" border=0><TBODY><TR><TD vAlign=top><BR><P><SPAN class=TableCap>Table 1</SPAN> <STRONG>Inject Simplification<BR></STRONG><BR>EJB 3.0's principle of dependency injection simplifies the use of resources and EJB references. Annotation of the injection target or specification of the target in the ejb-jar.xml descriptor provides for the occurrence of resources or references. Compare the differences.</P></TD></TR></TBODY></TABLE><!-- End Outer Table --><!-- Start Table Caption--></TD></TR></TABLE></FORM></P><P><BR>For example, if you are using the CartEJB in another EJB named OrderBean, with EJB 2.x you have to make a reference to the CartEJB in the deployment descriptor using ejb-ref: </P><PRE class=codesnippet>&lt;ejb-ref-name&gt;MyCart
&lt;/ejb-ref-name&gt;
	&lt;ejb-ref-type&gt;Session
	&lt;/ejb-ref-type&gt;
	&lt;home&gt;CartHome&lt;/home&gt;
	&lt;remote&gt;Cart&lt;/remote&gt;</PRE><P>Do a lookup of the home interface for the CartEJB, using JNDI, and create an instance of the CartEJB: </P><PRE class=codesnippet>Object homeObject = 
context.lookup(
	"java:comp/env/MyCart");
CartHome home = (
	CartHome)PortableRemoteObject.
	narrow(homeObject, 
	CartHome.class);
	Cart cart =(
		Cart)PortableRemoteObject.
		narrow(home.create(), 
		Cart.class);
cart.addItem("Item1");</PRE><P>After migration to the EJB 3.0 injection pattern, this code can become much simpler and would look like this: </P><PRE class=codesnippet>@EJB Cart cart;
	public void addItems() { 
		cart.addItem("Item1");
		}</PRE><P>A session bean can also be used as a façade for CMP entity beans, giving rise to a migration strategy for moving CMP entity beans to the EJB 3.0 persistence API. <BR></P><P><SPAN class=subhead>More Migrations</SPAN><BR>Migrating Message-Driven Beans (MDBs) to use EJB 3.0 is by far the easiest migration task. In EJB 3.0, MDBs do not have to implement the javax.ejb.MessageDriven interface but can instead be annotated with @MessageDriven. Resources and EJB references can be injected into MDBs in the same way as session beans. Similarly, you can inject the context (MessageDrivenContext for MDB) into the message-driven bean. <A href="javascript:openWindow2();"><FONT color=#002c99>Table 2</FONT></A> summarizes the changes in MDB between EJB 2.x and EJB 3.0.<BR><TABLE style="BORDER-RIGHT: #666666 1px solid; BORDER-TOP: #666666 1px solid; BORDER-LEFT: #666666 1px solid; BORDER-BOTTOM: #666666 1px solid" cellSpacing=2 cellPadding=4 width="100%" border=0><TBODY><TR bgColor=#333399><TH class=tabletext style="COLOR: #ffffff" vAlign=top align=left>EJB 2.x</TH><TH class=tabletext style="COLOR: #ffffff" vAlign=top align=left>EJB 3.0</TH></TR><!-- Start Content of Table --><TR bgColor=#cccccc><TD class=tabletext vAlign=top>Implements javax.ejb.MessageDriven</TD><TD class=tabletext vAlign=top>POJO: May annotate with @MessageDriven</TD></TR><TR bgColor=#ffffcc><TD class=tabletext vAlign=top>Specify destination type, name, and so on in the deployment descriptor.</TD><TD class=tabletext vAlign=top>May be annotated with @ActivationConfigProperty</TD></TR><TR bgColor=#cccccc><TD class=tabletext vAlign=top>MessageDrivenContext is acquired using setMessageDrivenContext().</TD><TD class=tabletext vAlign=top>MessageDrivenContext is achieved using dependency injection—for example: @Inject javax.ejb.MessageDrivenContext mc; </TD></TR><TR bgColor=#ffffcc><TD class=tabletext vAlign=top>Resource usage, such as Queue or Topic, is required resource-ref in the deployment descriptor and JNDI lookup.</TD><TD class=tabletext vAlign=top>Can be used using dependency injection: @Inject private Queue destQueue; </TD></TR><!-- End Content of Table --></TBODY></TABLE><TABLE cellSpacing=0 cellPadding=0 width="100%" border=0><TBODY><TR><TD vAlign=top><BR><P><SPAN class=TableCap>Table 2</SPAN> <STRONG>MDB Migration</STRONG><BR>MDBs do not have to implement the javax.ejb.MessageDriven interface in EJB 3.0, but they can instead be annotated with @MessageDriven. </P></TD></TR></TBODY></TABLE><BR></P><P><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --></P><P><BR><BR><BR><BR><BR><BR><BR><BR><BR>Persistence is one of the greatest challenges facing Java developers, compounded further by the lack of a standard persistence API for the J2EE platform. J2EE applications typically use one of these persistence choices: EJB 2.x CMP entity beans; a POJO persistence framework such as Oracle TopLink, JBoss Hibernate, or another custom O/R mapping framework; Data Access Objects with JDBC; and Java Data Objects (JDO). </P><P>Of these options, migrating EJB 2.x CMP entity beans presents the most interesting case because the change in programming model brings the EJB 3.0 persistence API to the same level of "POJOness" as other persistence solutions, which is worth taking some time to explore. </P><P>EJB 2.x CMP entity beans represent coarse-grained data objects and follow the distributed component model that includes security, transaction, and concurrency management. Because of these facilities they also carry the container management costs associated with it. EJB 3.0 entities are lightweight objects that can model fine-grained data. The difference between the 2.x and 3.0 models extends beyond simply converting the classes but should be considered at the modeling domain level. This difference does not mean that they cannot be converted, but that they should be migrated with some forethought and consideration of how they might best be remodeled. </P><P>Let's take a detailed look at some of the issues you will encounter as you migrate an EJB 2.x CMP entity to use the new EJB persistence API. </P><P><EM>Remodel your entities.</EM> With EJB 3.0 entities becoming POJOs, it is probably the best idea to remodel your entities and leverage richer OO modeling benefits such as inheritance, polymorphism, and so on. The details of remodeling of entity beans vary from one application to another and are beyond the scope of this article. </P><P><EM>Data transfer objects.</EM> The Data Transfer Object (DTO) pattern is a common pattern that allows data from entities to be accessible directly to nonlocal clients outside a transaction. It involved creating simple container objects to hold the entity data and transferring them to any tier as necessary. The EJB 3.0 specification makes the DTO pattern unnecessary because entities can already be shipped wherever they are required. The EntityManager API that is used for CRUD operations for entities allows detachment and merging of detached objects. An object can be detached from persistence storage and can be sent to the client, and then the changes can be merged/synchronized using the EntityManager.merge() method after the client sends it back to the server making updates to the object locally. </P><P>If DTOs already exist in an application, it is quite likely that they are the shortest path to making the EJB 2.x entities POJOs. The DTO becomes the candidate for the new entity bean if you simply add the logic into it and annotate/map it accordingly. </P><P><SPAN class=subhead>More Issues</SPAN><BR><EM>Migration of component interfaces.</EM> Business interfaces are optional for EJB 3.0 entity beans, and if they exist, they are only regular Java interfaces (POJIs). You can either convert existing local or remote component interfaces to regular Java interfaces by no longer extending the javax.ejb.EJBObject or EJBLocalObject interfaces, or discard them entirely. </P><P><EM>Conversion to POJO.</EM> EJB 3.0 entity beans no longer expect that the bean class implements the javax.ejb.EntityBean interface. They may extend any class and implement virtually any interface. The EntityBean interface can be removed from the implements clause. You have to convert the entity class and the get/set access methods from abstract to concrete. The previously virtual container-managed fields (cmp-fields) should have concrete implementations added to them. <BR></P><P><EM>EntityContext.</EM> Lightweight entities now inherit their context from the calling method and have no context of their own. You must remove the EntityContext and replace use of the context by any methods with suitable alternatives. For example, the getPrimaryKey() method would be replaced with a method on the bean or business interface that exposes the primary key to the user. <BR></P><P><!-- start 336x280 ad --><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --></P><P><EM>Home interfaces.</EM> The typed local and remote home interfaces in EJB 2.x are replaced by functionality in the EntityManager. Local and remote home interfaces should no longer be used, and clients that look them up should be converted to call the EntityManager directly to obtain or access the bean. </P><P>There are four different types of methods in the home interface: create methods, remove methods, home methods, and finders. The create methods can be converted easily into constructors that initialize the state information on the instance. The remove methods should be redirected to the EntityManager. The home methods may remain as they are because they can be invoked on any instance, regardless of the state of the instance. I'll discuss Finder methods later. </P><P>Optionally, you can keep the local home interface that clients use as a helper class for finders and factory style creation to minimize client code change and produce a nice clean usage pattern for your entity bean. </P><P><EM>Migration of life-cycle methods.</EM> EJB 2.x has a strict requirement to implement the EntityBean interface that defined the complete suite of life-cycle methods. Some of these life-cycle events no longer apply. <A href="javascript:openWindow3();"><FONT color=#002c99>Table 3</FONT></A> suggests strategies for migrating existing implementations of these methods. <BR></P><P><EM><TABLE style="BORDER-RIGHT: #666666 1px solid; BORDER-TOP: #666666 1px solid; BORDER-LEFT: #666666 1px solid; BORDER-BOTTOM: #666666 1px solid" cellSpacing=2 cellPadding=4 width="100%" border=0><TBODY><TR bgColor=#333399><TH class=tabletext style="COLOR: #ffffff" vAlign=top align=left>EJB 2.x life-cycle method</TH><TH class=tabletext style="COLOR: #ffffff" vAlign=top align=left>What to do in EJB 3.0</TH></TR><!-- Start Content of Table --><TR bgColor=#cccccc><TD class=tabletext vAlign=top>ejbCreate()</TD><TD class=tabletext vAlign=top>Implement logic in init methods/constructors.</TD></TR><TR bgColor=#ffffcc><TD class=tabletext vAlign=top>ejbPostCreate()</TD><TD class=tabletext vAlign=top>Implement logic in a constructor, or create a business method and annotate it as @PostPersist.</TD></TR><TR bgColor=#cccccc><TD class=tabletext vAlign=top>ejbRemove()</TD><TD class=tabletext vAlign=top>Create a business method, and annotate it with @PreRemove.</TD></TR><TR bgColor=#ffffcc><TD class=tabletext vAlign=top>setEntityContext() <BR>unSetEntityContext()</TD><TD class=tabletext vAlign=top>No longer applies.</TD></TR><TR bgColor=#cccccc><TD class=tabletext vAlign=top>ejbActivate()</TD><TD class=tabletext vAlign=top>Create a business method, and annotate it as @PostLoad</TD></TR><TR bgColor=#ffffcc><TD class=tabletext vAlign=top>ejbPassivate()</TD><TD class=tabletext vAlign=top>No longer applies (it can be removed).</TD></TR><TR bgColor=#cccccc><TD class=tabletext vAlign=top>ejbStore()</TD><TD class=tabletext vAlign=top>Create a business method, and annotate it with either @PrePersist or @PreUpdate.</TD></TR><!-- End Content of Table --></TBODY></TABLE><TABLE cellSpacing=0 cellPadding=0 width="100%" border=0><TBODY><TR><TD vAlign=top><BR><P><SPAN class=TableCap>Table 3</SPAN> <STRONG>Migration Strategies</STRONG><BR>Because some of the life-cycle events in EJB 2.x no longer apply, here are some possible strategies for migrating existing implementations of these life-cycle methods.<BR></P></TD></TR></TBODY></TABLE>Migration of O/R mapping.</EM> Before EJB 3.0, O/R mappings were always in the domain of the vendor and, as such, were typically stored in a vendor-specific deployment descriptor. Now that the O/R mappings have been integrated into the EJB standard, they can be defined either as annotations on the bean class or in a standard XML file. Most vendors should be providing ways to translate their mappings into the standard EJB mappings, either through a migration tool or from their graphical mapping editors. </P><P><EM>Exception handling.</EM> Exceptions are in a different Java package and are not checked; the same CreateException, RemoveException, and FinderException catch phrases do not apply. </P><P><SPAN class=subhead>Even More Issues</SPAN><BR><EM>Migration of finder methods.</EM> In EJB 2.x, the finder methods were defined in a home interface, and the EJB QL query for the finder was specified in the deployment descriptor. There are a few options for migrating EJB 2.x entity bean finders to EJB 3.0. The finder method can become a method in any class, and the EJB QL can be incorporated into either a named query or a dynamic query. Here is a finder method example defined in EJB 2.x in the deployment descriptor:</P><PRE class=codesnippet>&lt;query&gt;
	&lt;query-method&gt;
		&lt;method-name&gt;findAllByName
		&lt;/method-name&gt;
		&lt;method-params&gt;
			&lt;method-param&gt;String
			&lt;/method-param&gt;
		&lt;/method-params&gt;
	&lt;/query-method&gt;
	&lt;ejb-ql&gt;
		SELECT OBJECT(c) FROM 
		Customer c WHERE c.name LIKE 
		?1
	&lt;/ejb-ql&gt;
&lt;/query&gt;</PRE><P>A named query would look like this: </P><PRE class=codesnippet>public Collection findAllByName(
	String nameString) {
	return getEntityManager()
	.createNamedQuery(
		"findAllByName")
		.setParameter(
			"custName", nameString)
		.getResultList();
}</PRE><P>This example assumes that a NamedQuery annotation is defined this way: </P><PRE class=codesnippet>@NamedQuery(
	name="findAllByName", 
		queryString=
		"SELECT OBJECT(c) FROM 
		Customer c WHERE c.name 
		LIKE :custName")<BR><P><EM>Container-managed relationships.</EM> In EJB 2.x, the container was responsible for updating the other side of a relationship when one side was updated. EJB 3.0 opted to require the bean to manage its own relationships (to allow out-of-container entity testing and the like). Therefore, you must add management code to beans that have existing container-managed relationships, which is typically a one-line code change: </P><PRE class=codesnippet>public addOrder(Order order) {
	getOrders().add(order);
}</PRE><P>Adding the management code would require the backpointer assignment: </P><PRE class=codesnippet>public addOrder(Order order) {
	getOrders().add(order);
	order.setCustomer(this);
}</PRE><P><EM>Migrating CMP clients.</EM> In EJB 2.x, local and/or remote clients accessed entity beans. A well-documented best practice was to keep the entity beans local and wrap them in a session façade. In EJB 3.0, entities, being regular Java objects, are always local and can never be remote (RMI) objects. They are obtained and queried for through the EntityManager API. Client code (a session bean in the case of a session façade) must change its use of entity home or component-specific methods to use the EntityManager to access the entities on which it operates. </P><P>Local and remote entity references used to be required declarations in the deployment descriptor. In the client code the home would be looked up in JNDI, and home operations such as creates and finds could then be performed. Here's an example of a client lookup: </P><PRE class=codesnippet>public void createNewCustomer(
	String name, String city) 
throws CreateException, 
	NamingException, 
	RemoteException {
InitialContext ctx = new 
	InitialContext();
CustomerHome home = 
	(CustomerHome) ctx.lookup(
	"java:comp/env/ejb/CustomerHome"
	);
CustomerLocal customer = null;
	customer = home.create(
	name, city);
}</PRE><P>The migrated version of this code would look like this: </P><PRE class=codesnippet>@Resource private EntityManager 
	em;
public void createNewCustomer(
	String name, String city) {
		Customer cust = new 
		Customer();
		cust.setName(name);
		cust.setCity(city);
		em.persist(cust);
}</PRE><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --><P>It is clear from the preceding discussion that migrating EJB 2.x entity beans to EJB 3.0 is the most complex task, will have an impact on clients, and needs careful planning. </P><P><EM>Migrating POJO applications.</EM> Some frameworks have been persisting Java objects to relational databases for a long time, and vast numbers of applications are written by use of O/R frameworks. Applications that are currently using a POJO persistence framework are well positioned to migrate to the EJB persistence API because the domain objects are already Java objects. Further, the persistence frameworks' transaction mechanisms and session-level APIs are similar to those of the new EJB persistence API. </P><P>The result is that very little code rework is required. If you are currently evaluating a persistence framework for your J2EE applications, probably using a POJO persistence framework is the best choice because it can easily get you to the EJB 3.0 Persistence API. </P><P><SPAN class=subhead>Get to It</SPAN><BR>Change the session APIs used in the persistence framework to EntityManager APIs.</P><P>Change proprietary O/R XML to O/R mapping annotations or O/R XML defined by the EJB 3.0 persistence API. </P><P>The technical details of migration of POJO persistence to EJB 3.0 are covered in the article, "Prepare for the New EJB 3.0 Persistence API" (FTPOnline, June 2005)—see Resources online at www.javapro.com. </P><P>Migration of J2EE applications to EJB 3.0 is certainly not rocket science; however, like any other migration effort, it takes education, resources, and planning. The first step is to become aware of where the platform is moving and understand where it plans to end up. Once you understand the technology, you should undertake planning to decide the best strategy for getting there. </P><P>The prudent approach to migration is to migrate a subsection of the application before applying the practices to the whole. This strategy will ferret out many of the difficult migration issues and allow for experimentation to discover which approach might be best suited to the application. Features such as interoperability between EJB 2.x and EJB 3.0 components are critical when you're pursuing this kind of strategy. You can start trying out EJB 3.0 with an early implementation, and get ready for migrating to EJB 3.0.</P><P><SPAN class=AboutAuthor>About the Author</SPAN><BR>Debu Panda is a principal product manager of the Oracle Application Server development team and is responsible for the EJB container and Transaction Manager. Visit his <A href="http://radio.weblogs.com/0135826/" target=_blank><FONT color=#002c99>J2EE-focused blog</FONT></A>, or contact Debu at <A href="mailto:debabrata.panda@oracle.com"><FONT color=#002c99>debabrata.panda@oracle.com</FONT></A>. </P><!-- End Content Here --><!-- If there is only 1 page, delete the function --></PRE><!-- End Content Here --><!-- If there is only 1 page, delete the function --></PRE></SPAN><!-- End Content Here --><!-- If there is only 1 page, delete the function --><img src ="http://www.blogjava.net/mstar/aggbug/8588.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-28 08:21 <a href="http://www.blogjava.net/mstar/archive/2005/07/28/8588.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]The Preferences API in Java 2SE 1.4 (Merlin)</title><link>http://www.blogjava.net/mstar/archive/2005/07/15/7769.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 15 Jul 2005 05:35:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/15/7769.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/7769.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/15/7769.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/7769.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/7769.html</trackback:ping><description><![CDATA[<A href="http://www.onjava.com/pub/a/onjava/synd/2001/10/17/j2se.html?page=1">http://www.onjava.com/pub/a/onjava/synd/2001/10/17/j2se.html?page=1</A><BR>by <A href="http://www.onjava.com/pub/au/234">Jeff Brown</A><BR>10/17/2001 
<P><I>Editor's note: the following is a syndicated feature, which first published in Object Computing's "Java News Brief -- Java Technical Insight of the Month."</I></P>
<P>
<TABLE cellSpacing=0 cellPadding=8 width=336 align=right border=0>
<TBODY>
<TR>
<TD><!-- dy --><NOSCRIPT>
				<a href="http://ad.doubleclick.net/jump/onjava.ds/j2seart;abr=!ie;pos=_j2seart;sz=336x280;ord=2189237135?"><img src="http://ad.doubleclick.net/ad/onjava.ds/j2seart;abr=!ie;pos=_j2seart;sz=336x280;ord=2189237135?" border="0" width="336" height="280" alt="Advertisement" /></a>
				</NOSCRIPT></TD></TR></TBODY></TABLE>Starting with J2SE 1.4, the standard set of APIs will include the Preferences API made up of classes and interfaces in the <CODE>java.util.prefs</CODE> package. The Preferences API has been designed to provide a platform independent interface for storing preferences data. This article explores the Preferences API as it is implemented in the J2SE 1.4 Beta 2. Note that some details of the implementation may change in the final release of J2SE 1.4. </P>
<H3>Preferences Overview</H3>
<P>The Preferences API allows preference data to be divided into two categories, "System" preferences and "User" preferences. It is left to the application developer to categorize each preference into one of these. Generally system preferences include system wide attributes such as port numbers and other data that should be shared by all users of the system. User preferences include things like font or color preferences that may be user dependant.</P>
<P>Each of the two preference categories are structured as a hierarchical tree to allow for fine grained organization of the data. For example, under either of the two preferences trees there might be a hierarchy like <CODE>apps/JAsteroids/HighScores/</CODE> or <CODE>apps/JAsteroids/ScreenPreferences/</CODE>. In this case, the "apps" node is the parent node to the "JAsteroids" node and the "JAsteroids" node is the parent node to both the "HighScores" node and the "ScreenPreferences" nodes. </P>
<P>Any node in a preference tree may contain other nodes and may contain preference data. The types of data that can be stored as preferences are... </P>
<UL>
<LI><CODE>int</CODE> 
<LI><CODE>float</CODE> 
<LI><CODE>long</CODE> 
<LI><CODE>double</CODE> 
<LI><CODE>boolean</CODE> 
<LI><CODE>byte[]</CODE> 
<LI><CODE>String</CODE></LI></UL>
<H3>The API</H3>
<H4>Retrieving A Preferences Object</H4>
<P>An instance of <CODE>java.util.prefs.Preferences</CODE> represents a particular node in one of the two preference trees. As <CODE>Preferences</CODE> is an abstract class, an instance may not be created directly but instead should be retrieved from one of the static methods in the <CODE>Preferences</CODE> class.</P><PRE><CODE>// retrieve a reference to the root 
// of the user preferences tree
Preferences userPreferencesRoot = Preferences.userRoot();

// retrieve a reference to the root of 
// the system preferences tree
Preferences sysPreferencesRoot = Preferences.systemRoot();</CODE></PRE>
<P>These methods return references to the root node of the user preference tree and the system preference tree respectively.</P>
<P>There are two other static methods in the <CODE>Preferences</CODE> class that return a <CODE>Preferences</CODE> instance, <CODE>Preferences.userNodeForPackage(Object)</CODE> and <CODE>Preferences.systemNodeForPackage(Object)</CODE>. These methods return a <CODE>Preferences</CODE> instance that is associated with the argument's package. </P><PRE><CODE>package com.ociweb.preferencestest;
  
import java.util.prefs.*;
  
public class PreferenceTestFrame extends JFrame {
  
  // A reference to Preferences object
  private Preferences myPreferences = null;
  
  public PreferenceTestFrame() {
    super("Preferences Test Window");
  
    // Obtain a references to a Preferences object
    myPreferences = Preferences.userNodeForPackage(this);
  }
}</CODE></PRE>
<P>This <CODE>PreferenceTestFrame</CODE> class is defined to be in the <CODE>com.ociweb.preferencestest</CODE> package. The call to <CODE>Preferences.userNodeForPackage(this)</CODE> returns a <CODE>Preferences</CODE> object that is associated with the <CODE>com/ociweb/preferencestest/</CODE> node in the user preferences tree. If that node does not exist it will be created.</P>
<P>Two instance methods in the <CODE>Preferences</CODE> class that return references to other <CODE>Preferences</CODE> objects are <CODE>parent()</CODE> and <CODE>node(String)</CODE>. The <CODE>parent()</CODE> method returns a <CODE>Preferences</CODE> object that is associated with the parent node of the node on which the method was called. This method returns <CODE>null</CODE> if called on one of the root nodes. The <CODE>node(String)</CODE> method returns a <CODE>Preferences</CODE> object associated with some node relative to the node the method was called on. For example, if the <CODE>Preferences</CODE> object <CODE>P1</CODE> is associated with the <CODE>apps/JAsteroids/</CODE> node, then a call to <CODE>P1.node("ScreenPreferences/")</CODE> returns a <CODE>Preferences</CODE> object associated with the <CODE>apps/JAsteroids/ScreenPreferences/</CODE> node. If the requested node does not exist, it will be created.</P>
<H4>Storing And Retrieving Data</H4>
<P>Each piece of preference data stored using the Preferences API is stored in a particular node in one of the two preference trees and has a <CODE>String</CODE> key associated with it which must be unique within that particular node of the tree. There are 7 "get" methods and 7 "put" methods in the <CODE>Preferences</CODE> class, 1 method for each of the valid preference types.</P>
<UL>
<LI><CODE>int getInt(String key, int default)</CODE> 
<LI><CODE>float getFloat(String key, float default)</CODE> 
<LI><CODE>long getLong(String key, long default)</CODE> 
<LI><CODE>double getDouble(String key, double default)</CODE> 
<LI><CODE>boolean getBoolean(String key, boolean default)</CODE> 
<LI><CODE>byte[] getByteArray(String key, byte[] default)</CODE> 
<LI><CODE>String get(String key, String default)</CODE> </LI></UL>
<UL>
<LI><CODE>void putInt(String key, int value)</CODE> 
<LI><CODE>void putFloat(String key, float value)</CODE> 
<LI><CODE>void putLong(String key, long value)</CODE> 
<LI><CODE>void putDouble(String key, double value)</CODE> 
<LI><CODE>void putBoolean(String key, boolean value)</CODE> 
<LI><CODE>void putByteArray(String key, byte[] value)</CODE> 
<LI><CODE>void put(String key, String value)</CODE></LI></UL>
<P>Each of the "get" methods accepts a default value. This value will be returned if the specified <CODE>key</CODE> does not exist at that particular node. They <CODE>keys()</CODE> method returns an array of <CODE>String</CODE> objects that includes all of the keys stored at that particular node. </P>
<P>Note the absence of support for storing arbitrary objects and collections of objects. The API is designed to support simple data, not complex objects. A simple collection of attributes could be stored as a tokenized <CODE>String</CODE>. For example, if an application needs to store the location and size of a window the application could store 4 separate pieces of data (x-location, y-location, width and height) or the application could store a tokenized <CODE>String</CODE> that contains all 4 pieces of data and that <CODE>String</CODE> would need to be parsed after being retrieved. </P>
<P><CODE>package com.ociweb.preferencestest;<BR><BR>import java.awt.event.*;<BR>import java.util.prefs.*;<BR>import javax.swing.*;<BR><BR>/**<BR>* A frame to test some basic functionality <BR>* in the Preferences API<BR>*<BR>* @author Object Computing Inc.<BR>*/<BR>public class PreferenceTestFrame extends JFrame {<BR><BR>// A reference to a Preferences object<BR>private Preferences myPreferences = null;<BR><BR>// Default values for this frame's preferences<BR>public static final int DEFAULT_WINDOW_X = 50;<BR>public static final int DEFAULT_WINDOW_Y = 50;<BR>public static final int DEFAULT_WINDOW_WIDTH = 300;<BR>public static final int DEFAULT_WINDOW_HEIGHT = 100;<BR><BR>// Keys for this frame's preferences<BR>public static final String WINDOW_X_KEY = "TEST_WINDOW_X";<BR>public static final String WINDOW_Y_KEY = "TEST_WINDOW_Y";<BR>public static final String WINDOW_WIDTH_KEY = "TEST_WINDOW_WIDTH";<BR>public static final String WINDOW_HEIGHT_KEY = "TEST_WINDOW_HEIGHT";<BR><BR>public PreferenceTestFrame() {<BR>super("Preferences Test Window");<BR><BR>// Obtain a references to a Preferences object<BR>myPreferences = Preferences.userNodeForPackage(this);<BR><BR>// Retrieve the location of the frame from the Preferences object<BR>int windowX = myPreferences.getInt(WINDOW_X_KEY, DEFAULT_WINDOW_X);<BR>int windowY = myPreferences.getInt(WINDOW_Y_KEY, DEFAULT_WINDOW_Y);<BR><BR>// Retrieve the size of the frame from the Preferences object<BR>int windowWidth = <BR>myPreferences.getInt(WINDOW_WIDTH_KEY, DEFAULT_WINDOW_WIDTH);<BR>int windowHeight = <BR>myPreferences.getInt(WINDOW_HEIGHT_KEY, DEFAULT_WINDOW_HEIGHT);<BR><BR>// Set the size and location of the frame<BR>setBounds(windowX, windowY, windowWidth, windowHeight);<BR><BR>addWindowListener(new WindowAdapter() {<BR>public void windowClosing(WindowEvent we) {<BR>exitApp();<BR>}<BR>});<BR>}<BR><BR>private void exitApp() {<BR>// Save the state of the window as preferences<BR>myPreferences.putInt(WINDOW_WIDTH_KEY, getWidth());<BR>myPreferences.putInt(WINDOW_HEIGHT_KEY, getHeight());<BR>myPreferences.putInt(WINDOW_X_KEY, getX());<BR>myPreferences.putInt(WINDOW_Y_KEY, getY());<BR>System.exit(0);<BR>}<BR><BR>public static void main(String[] args) {<BR>new PreferenceTestFrame().show();<BR>}<BR>}</CODE></P><!--  sidebar begins  --><!--  sidebar ends  -->
<P>This application exercises the Preferences API by storing its window size and location as preferences and then retrieving them each time the application is executed so the window is restored to its previous location. The <CODE>Preferences</CODE> object is retrieved by calling <CODE>Preferences.userNodeForPackage(this)</CODE> so the data will be stored at the <CODE>com/ociweb/preferencestest/</CODE> node in the user preferences tree.</P>
<P>This implementation stores each of the window attributes as a separate preference. Alternatively, the implementation could have combined all of these pieces of information into a <CODE>String</CODE> formed something like <CODE>&lt;x-location&gt;:&lt;y-location&gt;:&lt;width&gt;:&lt;height&gt;</CODE>.<BR></P>
<H3>Where Is The Data?</H3>
<P>The details about where the data is actually stored may vary from platform to platform. Each of the static methods in the <CODE>Preferences</CODE> class that returns a <CODE>Preferences</CODE> object are relying on a factory that creates the <CODE>Preferences</CODE> object. This factory is some concrete implementation of the <CODE>java.util.prefs.PreferencesFactory</CODE> interface. The name of the concrete factory class to use is specified by the <CODE>java.util.prefs.PreferencesFactory</CODE> system property. The documentation explicitly states that this is <STRONG>NOT</STRONG> part of the specification and is subject to change in future releases. On Linux, the value of this system property is <CODE>java.util.prefs.FileSystemPreferencesFactory</CODE> so the Preferences API will use an instance of that class whenever an instance of <CODE>Preferences</CODE> is requested via one of the static methods in the <CODE>Preferences</CODE> class. This particular implementation stores all of the preference data in files on the file system. On Windows 2000, the value of this system property is <CODE>java.util.prefs.WindowsPreferencesFactory</CODE>. This implementation stores all of the preference data in the Windows Registry. Specifically, system preferences are stored under <CODE>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs</CODE> and user preferences are stored under <CODE>HKEY_CURRENT_USER\Software\JavaSoft\Prefs</CODE>. 
<TABLE cellSpacing=0 cellPadding=8 width=336 align=center border=0>
<TBODY>
<TR>
<TD><!-- dy --><NOSCRIPT>
				<a href="http://ad.doubleclick.net/jump/onjava.ds/j2seart;abr=!ie;pos=_j2seart;sz=336x280;ord=1640099687?"><img src="http://ad.doubleclick.net/ad/onjava.ds/j2seart;abr=!ie;pos=_j2seart;sz=336x280;ord=1640099687?" border="0" width="336" height="280" alt="Advertisement" /></a>
				</NOSCRIPT></TD></TR></TBODY></TABLE><BR clear=all><!--  me  --><IMG height=326 alt="Windows Registry Screenshot" src="http://www.onjava.com/onjava/synd/2001/10/17/graphics/jnbSep2001-1.gif" width=469 border=0></P>
<P>This screen shot shows the data stored by the <CODE>PreferenceTestFrame</CODE> application shown above. Of course the application code makes no references to the Windows Registry. The application is written to the standard Preferences API.</P>
<P>There are facilities built into the Preferences API to allow preference data to be exported and imported to and from an eXtensible Markup Language (XML) file.</P><!--  sidebar begins  --><!--  don't move sidebars  --><!--  sidebar ends  -->
<P><CODE>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<BR><BR>&lt;!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'&gt;<BR><BR>&lt;preferences EXTERNAL_XML_VERSION="1.0"&gt;<BR>&lt;root type="user"&gt;<BR>&lt;map /&gt;<BR>&lt;node name="com"&gt;<BR>&lt;map /&gt;<BR>&lt;node name="ociweb"&gt;<BR>&lt;map /&gt;<BR>&lt;node name="preferencestest"&gt;<BR>&lt;map&gt;<BR>&lt;entry key="TEST_WINDOW_WIDTH" value="256" /&gt;<BR>&lt;entry key="TEST_WINDOW_HEIGHT" value="314" /&gt;<BR>&lt;entry key="TEST_WINDOW_X" value="540" /&gt;<BR>&lt;entry key="TEST_WINDOW_Y" value="63" /&gt;<BR>&lt;/map&gt;<BR>&lt;/node&gt;<BR>&lt;/node&gt;<BR>&lt;/node&gt;<BR>&lt;/root&gt;<BR>&lt;/preferences&gt;</CODE></P>
<P>This XML was created by calling the <CODE>exportSubtree(OutputStream)</CODE> method on an instance of <CODE>Preferences</CODE> that was associated with the root of the user preference tree. These are the same preferences reflected in the Windows Registry screenshot shown above. The <CODE>exportSubTree(OutputStream)</CODE> method exports any particular node and all of its sub-nodes in the tree. The <CODE>exportNode(OutputStream)</CODE> method exports a particular node and does not include any of its sub-nodes. Each of those methods are instance methods as they operate on a particular node in the tree.</P>
<P>The static method <CODE>importPreferences(InputStream)</CODE> in the <CODE>Preferences</CODE> class imports preferences from an XML source and adds those preferences to the appropriate preference tree. If any of the nodes represented in the XML do not exist in the preference tree, they are created.</P>
<H3>Comparing With Property Files</H3>
<P>Without the Preferences API, many applications have taken advantage of Java Property files for storing simple data like that accounted for by the Preferences API. In comparison to using Property files, the Preferences API provides a more robust mechanism for organizing the data without introducing complexity. For example, Property files do not provide any easy way to group data hierarchically. This flexibility is built into the Preferences model. Property files are dependent on a file system, which may not exist on every platform with a Java VM, for example a cellular telephone. There is nothing about the Preferences API that depends on a file system. For platforms without a file system, an implementation of <CODE>java.util.prefs.PreferencesFactory</CODE> could be developed that stores the data using whatever mechanisms are appropriate for that platform.</P>
<H3>Summary</H3>
<P>The Preferences API provides a nice interface for storing application preferences in a platform independent way. While the details about the storage of the data vary from platform to platform, the application code is as portable as any other Java code.</P>
<P>This article has been written as of J2SE v1.4 beta 2. Keep an eye out for changes that may be made to the API before the final release of v1.4. </P><img src ="http://www.blogjava.net/mstar/aggbug/7769.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-15 13:35 <a href="http://www.blogjava.net/mstar/archive/2005/07/15/7769.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]适用于 Java 程序员的 CSP ，第 3 部分</title><link>http://www.blogjava.net/mstar/archive/2005/07/13/7584.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 13 Jul 2005 00:47:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/13/7584.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/7584.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/13/7584.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/7584.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/7584.html</trackback:ping><description><![CDATA[<A href="http://www-128.ibm.com/developerworks/cn/java/j-csp3.html">http://www-128.ibm.com/developerworks/cn/java/j-csp3.html</A><BR><SPAN class=atitle2>JCSP 的高级主题</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/java/j-csp3.html#author1"><NAME><FONT color=#002c99>Abhijit Belapurkar</NAME></FONT></A><BR>高级技术架构师, Infosys Technologies Limited<BR>2005 年 7 月 11 日</P>
<BLOCKQUOTE><ABSTRACT-EXTENDED>Abhijit Belapurkar 通过介绍 JCSP 开发高级主题，结束了由三部分组成的介绍适用于 Java 开发人员的 CSP 的系列文章，介绍的内容包括：JCSP 与 AOP 的相似性、JCSP 与 <CODE>java.util.concurrent</CODE> 的比较，以及用 JCSP 进行高级同步。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>如果您从开始就一直阅读这个系列，那么到了现在，您应当同意 CSP 是一个值得研究的编程范式，而 JCSP 库则值得您添加到工具包中。在第 3 部分中，我只是给您提供了更多的研究 JCSP 理由。我将从讨论 CSP 的复合技术与面向方面编程（或 AOP） 的模块化技术之间的相似性开始。然后介绍 JCSP 的一些更高级的同步构造，并讨论适合应用它们的场景，还将更广泛地讨论使用 JCSP 进行多线程编程的好处，比如验证、维护以及可应用于分布式编程的一种方法。</P>
<P>我还要回答以下问题：如果已经采用了 <CODE>java.util.concurrent</CODE> 的并发性工具，那么是否还有学习 JCSP 的必要。（显然，我认为有这个必要！）</P>
<P>如果还没有阅读本文的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 1 部分</FONT></A> 和 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 2 部分</FONT></A>，那么在继续我们的介绍之前，您可能需要这么做。</P>
<P><A name=IDATCQHB><SPAN class=atitle2>AOP 和 CSP</SPAN></A><BR>面向方面编程，即 AOP，是现在大多数 Java 开发人员都知道的一个术语。AOP 把系统当成方面或者关注点的的组合，而不是单纯地将系统看成是对象的组合。它试图通过把切入点组合成独立的模块，由模块代码解决一个特定关注，从而提高系统的模块性。这些模块可以用静态或动态编织技术编织在一起。</P>
<P>这个系列文章中已经介绍了很多内容，所以对您来说，应当很容易就可以看出 AOP 的概念 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">把模块编织在一起</I> 与 CSP 的概念 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">组合进程网络</I> 之间的相似性。两个技术之间的相似（绝对不是故意的！）主要是由于后者的复合性质。</P>
<P>一个基于安全性检测的示例被频繁地用作 AOP 的示例。在这个示例中，包含解决应用程序安全问题的代码的模块是以方面的形式编写和打包的；然后，这个方面将应用于整个应用程序中所有适当的业务对象。为了用 CSP 解决同样的问题，可以先从编写进程开始，编写一个包含业务对象的功能的进程，再编写另一个包含应用程序的安全性检测功能的进程。然后可以用 <CODE>One2One</CODE> 通道把两个进程连接起来。CSP 技术的优势与 AOP 技术类似：都可以将关注点分隔开。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=sidebar1><B>不要错过本系列其余部分！</B></A><BR>“适用于 Java 程序员的 CSP”是由三部分组成的介绍通信顺序进程（Communicating Sequential Processes —— CSP）的一个系列。CSP 是并发编程的一个范式，它承认了并发编程的复杂性，却没有把复杂性留给开发人员。请阅读本系列的其他部分：<BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 2 部分：用 JCSP 进行并发编程</FONT></A><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><A href="http://www-128.ibm.com/developerworks/cn/java/j-csp3.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 3 部分： JCSP 的高级主题</FONT></A></P></TD></TR></TBODY></TABLE></P>
<P><A name=IDAYDQHB><SPAN class=atitle2>高级 JCSP 同步</SPAN></A><BR>通道不是进程间同步的惟一可用选择，也不总是最合适的选择。通道总是在以下两个进程间同步：阅读器和写入器。是否在运行多个阅读器/写入器并不重要，因为在 <CODE>Any2One</CODE>、<CODE>One2Any</CODE> 和 <CODE>Any2Any</CODE> 模型中，每种类型的进程都只有一个进程能够积极地参与到通道两边进行的会话中。</P>
<P>为了获得更高级的同步，即在多个进程之间的同步，JCSP 提供了 <CODE>Barrier</CODE>、<CODE>Bucket</CODE> 和 <CODE>CREW</CODE> 构造，还提供了一个叫作 <CODE>ProcessManager</CODE> 的类负责管理 <CODE>CSProcess</CODE> 实例。</P>
<P><A name=IDACFQHB><SPAN class=atitle2>Barrier 构造</SPAN></A><BR><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">barrier</I> 是一个 JCSP 事件，可以充当多个进程之间的同步机制。每个 barrier 都能与多个进程相关联，任何在这个 barrier 上同步的进程都将被阻塞，直到其他进程已经同步为止。</P>
<P>必须用需要在 <CODE>Barrier</CODE> 类上同步的进程的数量来创建它，其中每个进程都运行在独立的线程中。在完成当前一轮工作之后，每个进程都调用 <CODE>Barrier</CODE> 实例的 <CODE>sync()</CODE> 方法，然后等候其他进程完成。</P>
<P><A name=IDAZFQHB><SPAN class=atitle3>Barrier 内部</SPAN></A><BR>从内部来看，<CODE>Barrier</CODE> 包含一个成员变量，在创建 <CODE>Barrier</CODE> 对象时，这个成员变量被初始化为指定的进程总数。每当进程调用 <CODE>sync()</CODE> 方法时，这个变量就减一，并发出 <CODE>wait()</CODE> 调用。（您会想起已经介绍过的内容：Java 线程在能够调用 <CODE>wait()</CODE> 方法之前必须一直持有恰当的锁。在这里，由于将 <CODE>Barrier</CODE> 的 <CODE>sync()</CODE> 方法标记为同步的，所以 <CODE>Barrier</CODE> 实例本身充当着锁的作用。） 检测是在最后一个调用 <CODE>sync()</CODE> 的进程所在的线程上进行的。如果成员变量变成 <CODE>0</CODE>，则清楚地表明，所有进程已经完成了对 <CODE>sync()</CODE> 的调用 ，可以安全的发出 <CODE>notifyAll()</CODE> 了。这时，内部的计数器被重新设置回最初提供的进程总数，从而使得 <CODE>Barrier</CODE> 实例可以在下一轮同步中重复使用。</P>
<P>注意，<CODE>Barrier</CODE> 并没有把进程的身份和自身关联起来。所以，只有能得到 <CODE>Barrier</CODE> 对象，任何进程（除了要在 Barrier 上同步的进程之外）都能调用对象上的<CODE>sync</CODE>，从而将一个或多个合法进程排除在外。要想防止这个问题，可以对 <CODE>Barrier</CODE> 对象的可见性进行严格控制，只允许那些已经在 Barrier 上同步的进程集看到它。</P>
<P><A name=IDAKYFOB><SPAN class=atitle3>似曾相识！</SPAN></A><BR>如果您认为 <CODE>Barrier</CODE> 工作方式的描述听起来很熟悉，那就对了。<CODE>Parallel</CODE> 构造自行控制运行多个 <CODE>CSProcess</CODE> 实例的基础就是 <CODE>Barrier</CODE> 构造的底层机制。</P>
<P>您会想起前面介绍过，在您调用 <CODE>Parallel</CODE> 实例上的 <CODE>run</CODE> 方法时，它创建了 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">(n-1)</I> 个线程来运行前面 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">(n-1)</I> 个 <CODE>CSProcess</CODE> 实例，然后在自己的线程中运行最后一个 <CODE>CSProcess</CODE> 实例。所有这些进程都在一个公共的 <CODE>Barrier</CODE> 实例上进行 <CODE>sync</CODE>，这个实例是在 <CODE>Parallel</CODE> 类的构造函数中创建的（而且通过负责运行 <CODE>CSProcess</CODE> 的每个 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n-1</I> 线程的构造函数，可以将实例传递给每个线程）。</P>
<P><CODE>Barrier</CODE> 是一个比 <CODE>Parallel</CODE> 级别低的构造，因此，相应地也就带来了更多的复杂性，进程可以在任意时间从 <CODE>Barrier</CODE> 对象 <CODE>enroll</CODE>（登记）或 <CODE>resign</CODE>（退出）。例如，在一个工作进程运行时，它可能想与时钟同步（实际上是模拟时钟滴答） ，然后停下来休息一段时间（在此期间，它应当从 barrier 退出），接着再登记到 barrier，重新开始工作。 </P>
<P><A name=IDA50FOB><SPAN class=atitle2>Bucket 构造</SPAN></A><BR><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">bucket</I> 也是一个用于多进程的同步 barrier。bucket 构造和 barrier 构造之间的区别是：在前者中，所有进程都被阻塞，直到另外一个（外部）进程决定释放它们，而释放是通过“清空 bucket”进行的。</P>
<P>使用 <CODE>Bucket</CODE> 的方法只是创建一个 bucket 构造；然后，每个需要在该构造上面同步的进程都可以调用这个 bucket 的 <CODE>fallInto()</CODE> 方法。<CODE>fallInto()</CODE> 是一个同步的方法，它维护一个内部变量，用该变量来跟踪当前（等候）的进程数量（也就是说，每有一个调用 <CODE>fallInto()</CODE> 方法的进程，计数器变量就加 1。）在计数增加之后，调用进程执行 <CODE>wait()</CODE>，这导致调用进程受阻塞。当外部进程调用 <CODE>Bucket</CODE> 的（也是同步的） <CODE>flush()</CODE> 方法时，就发送一个 <CODE>notifyAll()</CODE> ，唤醒所有受阻塞的线程。阻塞进程的最后计数将从这个调用中输出。</P>
<P><CODE>Barrier</CODE> 是确定性的，而 <CODE>Bucket</CODE> 是非确定性的。可以保证同步到 <CODE>Barrier</CODE> 的进程得到重新安排，从而得以执行（在一个时间周期后，受阻时间长短等于组中最慢的进程的执行时间），同步到 <CODE>Bucket</CODE> 的线程受阻塞的时间是不确定的，时间长短取决于清除进程何时决定发出释放这些进程的调用。</P>
<P>在必须按照先后顺序处理一组进程时，<CODE>Barrier</CODE> 很有用。这就是说，每个进行到步骤 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n</I> 的进程都不能进行步骤 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n+1</I>，直到同组中的其他所有进程都已经完成它们的步骤 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n</I>。在每个进程都要完成自己分配的任务并且停在 bucket 中，以表明自己可以进行下一步，这种情况下，<CODE>Barrier</CODE> 很有用。清除进程可以周期性地清除这个 bucket （可能用某些工作调度算法），从而释放目前在 bucket 中的所有进程，开始下一组任务。</P>
<P><A name=IDAL3FOB><SPAN class=atitle2>CREW 构造</SPAN></A><BR>并发读/排它写（Concurrent-Read-Exclusive-Write）构造，即 CREW 锁，允许多个并行阅读器访问共享资源，条件是：（1）“读”访问不会修改资源；（2）之前没有写入器正在访问资源。当写入器正在访问资源时，它对资源拥有排它权限：不管是阅读器还是写入器，都不允许访问资源。</P>
<P>CREW 锁的使用是为了消除争用风险（因为竞争的阅读器/写入器进程之间任意的交叉），并且由于允许多个阅读器进程并行执行而提高了性能。是否真的会有性能提升则取决于以下因素：以读模式访问资源的频率与以写模式访问资源的频率的比较、读操作和写操作的时间，以及在同一时间在冲突模式下试图访问共享资源的进程数量。</P>
<P>请参阅 JCSP 发行版附带的 Javadoc（在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp3.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>参考资料</FONT></A>中），以获得 CREW 锁的内部实现的精彩描述。</P>
<P><A name=IDAZ3FOB><SPAN class=atitle2>管理 CSProcess 实例</SPAN></A><BR>在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 2 部分</FONT></A> 中，我介绍了如何用 <CODE>Parallel</CODE> 构造把较低级别的“构造块”进程组合成更高级别的进程网络。虽然对于许多编程场景，这些描述都是合适的，但在这篇文章的示例中，我假设已经知道了需要创建的所有进程的全部细节。但是对于某些应用程序来说，可能会发现有必要根据只能在运行时才能计算的某些条件，动态地创建一个或多个进程，而且还应当能够管理它们。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=sidebar2><B>JCSP.net</B></A><BR>我在本文示例中使用的 JCSP 库实际上使用的是同一 JVM 中运行的线程，当然，JVM 可能正运行在单处理器或多处理器机器上。倘若您对是否有可能组合对跨许多 JVM 的进程进行通信和同步的分层网络感到怀疑，那么答案是肯定的！JCSP 库的叫作 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JCSP.net</I> 的一个变体可以让您精确地做到这点。使用 JCSP.net 编程的最大好处是：跨物理网络运行的虚拟通道看起来差不多就像您在本文中已经遇到过的本地的、单一 JVM 的通道一样，虽然很显然会更复杂一些。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp3.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>参考资料</FONT></A>，学习关于 JCSP.net 可用的商业版本的更多内容。</P></TD></TR></TBODY></TABLE></P>
<P>JCSP 为这类场景提供了一个叫作 <CODE>ProcessManager</CODE> 的类。这个类接受 <CODE>CSProcess</CODE> 实例作为输入参数，允许用以下两种不同的方法之一启动进程。如果在<CODE>ProcessManager</CODE> 上调用 <CODE>run()</CODE> 方法，那么 <CODE>ProcessManager</CODE> 将启动托管的进程，然后等候进程终止（被管理的进程在 <CODE>ProcessManager</CODE> 的线程上运行，所以，后者的运行被阻挡，直到托管的进程完成为止）。另一个选项是调用 <CODE>ProcessManager</CODE> 上的 <CODE>start()</CODE> 方法，这使托管的进程在独立的线程中启动，在这种情况下，<CODE>ProcessManager</CODE> 自己仍然继续运行。</P>
<P><A name=IDA45FOB><SPAN class=atitle3>管理并行优先级</SPAN></A><BR>我在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 2 部分</FONT></A> 中提到：<CODE>Parallel</CODE> 构造并行地运行构造函数中传递给它的数组中包含的所有 <CODE>CSProcess</CODE> 实例。但是在某些情况下，可能必须要为这些进程赋予不同的优先级。JCSP 提供了一个叫作 <CODE>PriParallel</CODE> 的类，可以为传递给它的进程加上优先级。</P>
<P>受控制的进程的优先级是由进程在数组中的位置索引决定的。在数组中，放在前面的进程拥有更高的优先级。注意，JCSP 以底层线程的优先级机制实现进程的优先级；所以优先级实际的工作方式取决于底层的 JVM 实现。</P>
<P><A name=IDA0AGOB><SPAN class=atitle2>越来越多的构造！</SPAN></A><BR>迄今为止讨论的构造（从第 2 部分开始到现在这里）都是 JCSP 库的核心构造。可以用它们来解决普通的和高级的并发性问题，现在应当可以让您开始编写所喜爱（也没有错误）的多线程程序了。当然，在 JCSP 库中，这些问题只是冰山之一角。请考虑以下有趣的构造的应用，您也可以自己对它们进行研究： </P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><B>BlackHoleChannel</B> 实现 <CODE>ChannelOutput</CODE> 接口，包含一个空的 <CODE>write()</CODE> 方法。可以把任意数量的数据写入这个通道。如果想把现有进程用在不同的环境中，而 <I>不</I> 再使用某些输出，那么 <CODE>BlackHoleChannel</CODE> 类是最有用的。因为不能一直留着某个输出通道不处理（害怕造成死锁），所以最好的选择就是让进程在 <CODE>BlackHoleChannel</CODE> 的实例上产生输出，而 BlackHoleChannel 则有效地充当存放所生成数据的透明无底洞。<BR><BR>
<LI><B>Paraplex</B> 是一个进程，它将其输入通道集上的多个对象流转化到单一输出通道。等到每个输入通道上都有一些可用输入之后（在输入到达的时候就接受，没有要求或规定特定的顺序），就可以将这些输入打包成数组（数据的尺寸与输入通道的数量相同），然后把数据通过输出通道发送为单一输出。如果需要用表格的形式表现某些数据，那么 <CODE>Paraplex</CODE> 可能会很方便。例如，假设有一个三列的表，每列均用数字填充。前两列用前面已经介绍过的 JCSP 进程生成的数字填充：<CODE>NumbersInt</CODE> 为第一列生成从 0 开始的自然数序列，<CODE>SquaresInt</CODE> 为第二列生成对应的平方序列。一个即插即用的叫作 <CODE>FibonacciInt</CODE> 的 JCSP 进程（随 JCSP 库一道立即可用）可以用来为第三列生成斐波纳契数系列。<BR><BR>可以容易地用一个组合了三个输入通道的 <CODE>ParaplexInt</CODE> 实例来处理这三个列。每个输入通道都要附着在前面提到过的三个进程中的一个的实例上。然后，<CODE>ParaplexInt</CODE> 的单一输出通道向 <CODE>CSProcess</CODE> 传送数据，后者则接着读取带有三个元素的数组，并在适当的格式化表格中输出它们。<BR><BR>
<LI><B>Deparaplex</B> 是一个类，它和 <CODE>Paraplex</CODE> 类相反， 它把数据从单一输入通道 <I>分离</I> 到一组输出通道。所以，<CODE>Deparaplex</CODE> 类可能读取一个数组对象（尺寸为 <I>n</I>），把数组中的每个元素逐个输出到它的 <I>n</I> 个输出通道上。然后，<CODE>Deparaplex</CODE> 进程会一直等候，直到它生成的每个元素都被写入的通道接受为止。</LI></UL>
<P>请参阅 JCSP 库的下载 （在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp3.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>参考资料</FONT></A> 中），以获得这结构造的更多文档，包括用例。</P>
<P><A name=IDAYDGOB><SPAN class=atitle2>CSP 的好处</SPAN></A><BR>CSP 是基于成熟的数学理论的并发编程范式。因此，CSP 提供了丰富的设计模式和工具集，可以防范常见的多线程缺陷，例如争用风险、死锁、活动锁和资源耗尽。因为所有这些数学上的完善性都构建到了 JCSP 库中，所以可以直接用它根据规定好的指导原则来编写应用程序。 （也就是说，不必非得理解理论才能利用它；虽然清楚的了解会更有优势！）。因为存在正式的针对 Java 的 CSP 模型，所以可以分析并正式地验证用 JCSP 构造构建的任何多线程 Java 应用程序。</P>
<P>正如前面所指出的，AOP 的许多好处也适应于基于 CSP 的程序。基于 CSP 的程序的主要好处是关注点的分享。可以使用 CSP 作为程序的基础（例如通过 JCSP 库），还可以确保进程之间干净的解耦，因为它们只能通过通道进行交互，不能通过直接的方法调用进行交互。它还确保可以完美地把数据和应用程序逻辑封装在通道接口之后的进程之中。所有的副作用都被限制在进程的边界之内，编程实体之间的交互都是显式公开的。在基于 JCSP 的程序中，没有隐藏的互交 —— 在网络的每一级上，所有的管道都是可见的。</P>
<P>CSP 的第二个好处是它的复合性质。进程可以组合，从而构成更复杂的进程（复杂的进程还可以再组合起来构成更加复杂的进程），很容易地随时间推移进行修改和扩展。所以，基于 CSP 的应用程序设计特别简单并且易于理解，并且在进行维护的时候也非常健壮。CSP 的复合性质也促进了更好的重用；正如在第 2 部分的编程示例中看到的，可以用大量不同的设置使用一个 JCSP 进程，如果需要的话，可以将不希望的输出重定向到黑洞中。</P>
<P>用 CSP 进行并发编程的最后一个好处对于构建分布式系统的开发人员来说特别明显。在本文中，我描述了实现并发性的不同选项（运行在单一进程中的多线程、运行在一个器上的多进程和运行在多个处理器上的多进程）。通常，这些并发机制中的每一个都要求使用完全不同的执行机制、编程接口和设计范式。而一旦用 CSP 开发应用程序，那么可以在不影响（至少不非常显著）应用程序设计或代码的情况下决定在哪个平台上运行应用程序，比如，是在多线程平台上，还是单一处理器上的多进程平台上，亦或是在分布处理器上运行的多进程平台上。</P>
<P><A name=IDACEGOB><SPAN class=atitle2>JCSP 和 java.util.concurrent</SPAN></A><BR>大多数 Java 程序员都知道，<CODE>java.util.concurrent</CODE> 包是作为 J2SE 1.5 类库的标准组成部分引入的。因为JCSP 库出现的时间比这个包加入 Java 平台的时间早，所以您可能想知道是否需要两者都用，或者说您想知道，既然已经适应了 <CODE>java.util.concurrent</CODE>，为什么还要费力地学习 JCSP。</P>
<P><CODE>java.util.concurrent</CODE> 包实际上是 Doug Lea 的一个很不错的流行的 <CODE>util.concurrent</CODE> 库的重新整理。这个包的设计目的是提供一个强壮的、高性能的标准化工具集，简化 Java 平台上的并发 —— 而这个目的已经实现了！<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">毫无疑问！</I> 如果 <CODE>java.util.concurrent</CODE> 包中的工具适合您的应用程序，那么请使用它们，您将得到回报。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=sidebar3><B>Java 中的通信线程</B></A><BR>JCSP 不是 Java 平台上惟一可用的 CSP 实现。Java 中的通信线程（CTJ） 是另一个流行的 CSP 实现，由 Twente 大学的 Gerald Hilderink 和 Andy Bakkers 开发。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp3.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>参考资料</FONT></A>，以获得 JCSP 和 CTJ 的详细比较，比如说，比较它们的哲学、相似与不同、性能和使用场景。</P></TD></TR></TBODY></TABLE></P>
<P>但是，正如我希望的，本文中的讨论已经显示出：基于 CSP 的技术（通过 JCSP 库） 表现出的机制超出了 <CODE>java.util.concurrent</CODE> 的范围。可以把系统构建成通信进程的网络，并用它们组合成网中之网，实现这一点的范式既自然又简单。这不仅提高了编写多线程应用程序的体验，而且提高了产品 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">品质</I>。用 JCSP 构建系统会产生干净的接口，很少有会造成产品不好维护和技术变化这类隐藏的副作用。正式的验证（即正式地推断应用程序的安全性和活动属性）对于安全敏感和高价值的财务应用程序来说也是强大的资产。</P>
<P>在许多情况下，这两个包不是互相排斥的：有些 JCSP 的核心要素非常有望会在 <CODE>java.util.concurrent</CODE> 提供的原子的低级机制顶部重新实现，新实现的开支可能要比目前使用的标准监视器机制的开支更少。</P>
<P><A name=IDA2FGOB><SPAN class=atitle2>第 3 部分的结束语</SPAN></A><BR>在这篇介绍适用于 Java 程序员的 CSP 的系列文章中，介绍了许多基础知识。在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 1 部分</FONT></A> 中，我介绍了 Java 语言目前支持的多线程编程的构造。还解释了这些构造的起源，讨论了它们与 Java 平台多线程编程的 4 个常见缺陷（争用风险、死锁、活动锁和资源耗尽）之间的关系。在对第一部分进行总结时，我解释了为什么在任意、大型、复杂的多线程应用程序中，很难应用正式的理论消除编程 bug 或证明它们不存在。我推荐使用 CSP 作为这个困境的备选项。</P>
<P>在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 2 部分</FONT></A> 中，我介绍了 CSP 理论和它最流行的一个基于 Java 的实现 —— JCSP 库。我用了几个实例演示了基本的 JCSP 构造（ 例如进程、通道和警卫等）的使用。</P>
<P>在最后这篇文章中，我介绍了 JCSP 中的高级主题，其中包括它与面向方面编程的相似性，以及它与 Java 平台的其他一些并发编程包的比较。我还演示了一些用来解决更复杂的同步问题的 JCSP 构造，简要讨论了用 JCSP.net 包进行分布式编程的可能性。</P>
<P>我希望我已经表达得够清楚，用 JCSP 编写多线程 Java 应用程序有许多优势。下次您会发现，在您自己考虑（也可能要在躲避）多线程应用程序设计时，您可能想转而采用 JCSP 库。JCSP 提供了非常简化的并发编程方法，生成的程序既能对最常见的多线程应用程序开发缺陷进行验证，还易于理解、调试和长远维护它们。</P>
<P><A name=IDAQGGOB><SPAN class=atitle3>致谢</SPAN></A><BR>非常感谢 Peter Welch 教授在我编写这个文章系列期间给予的鼓励。他在百忙之中抽出时间非常细致地审阅了草稿，并提供了许多宝贵的提高此系列文章的质量和准确性的建议。如果文章中还存在错误，那都是由于我的原因！我在文章中使用的示例基于或来自 JCSP 库的 javadoc 中提供的示例，以及 JCSP Web 站点上提供的 Powerpoint 演示文稿。这两个来源提供了需要探索的大量信息。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www-128.ibm.com/developerworks/java/library/j-csp3.html" target=_blank xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>英文原文</FONT></A>。<BR><BR>
<LI>Nicholas Lesiecki 撰写的“<A href="http://www-128.ibm.com/developerworks/cn/java/j-aspectj/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>使用面向 Aspect 的编程改进模块性</FONT></A>” （developerWorks，2002 年 1 月）第一次介绍了 AOP 的好处。<BR><BR>
<LI>请参阅 developerWorks 上为期一年的 <A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=AOP%40Work%3A" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I><FONT color=#002c99>AOP@Work</FONT></I></A> 系列，并从其中突出强调的部分学习更多关于方面编程的内容。 <BR><BR>
<LI>并发性专家 Brian Goetz 在“<A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp10264/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制</FONT></A>”（developerWorks，2004 年 10 月）这篇文章中解释了 <CODE>java.util.concurrent</CODE> 的一些优势与不足。<BR><BR>
<LI>Goetz 在“<A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11234/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 理论与实践: 流行的原子</FONT></A>” （developerWorks，2004 年 11 月）这篇文章中解释了 <CODE>java.util.concurrent</CODE> 中原子类的重要性。<BR><BR>
<LI>C.A.R. Hoare 撰写的“<A href="http://doi.acm.org/10.1145/359576.359585" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Communicating Sequential Processes</FONT></A>”把通信顺序进程的并行复合作为一个基本的编程结构化方法引入进来介绍（<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Communications of the ACM Archive</I>，1978）。<BR><BR>
<LI>可以免费获得 PDF 格式的 C.A.R. Hoare 撰写的 <A href="http://www.usingcsp.com/cspbook.pdf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>关于 CSP 的书籍</FONT></A>。<BR><BR>
<LI>Bill Roscoe 撰写的 <A href="http://www.amazon.com/exec/obidos/ASIN/0136744095/qid%3D1109670926/sr%3D11-1/ref%3Dsr%5F11%5F1/002-1631025-4649632" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Theory and Practice of Concurrency</FONT></A> （Prentice Hall, 1997） 是关于并发性和 CS 主题的最新书籍。<BR><BR>
<LI>牛津大学计算机实验室负责的 <A href="http://www.comlab.ox.ac.uk/archive/csp.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>CSP Archive</FONT></A> 是学习更多关于 CSP 内容的好地方，除此之外，还有 <A href="http://www.wotug.org/index.php" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>WoTUG homepage</FONT></A>。<BR><BR>
<LI><A href="http://www.cs.kent.ac.uk/projects/ofa/jcsp/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>JCSP home page</FONT></A> 由英国坎特伯雷市肯特大学负责。<BR><BR>
<LI>可以从 QuickStone 科技获得 <A href="http://www.quickstone.com/xcsp/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Network Edition of JCSP</FONT></A>。<BR><BR>
<LI><A href="http://www.fsel.com/fdr2_download.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>FDR2</FONT></A> （故障偏差求精，Failures-Divergence Refinement）是面向基于 CSP 的程序的几个商业化模型检测工具之一。<BR><BR>
<LI>Gerald Hilderink、Jan Broenink 和 Andre Bakkers 在“<A href="http://www.ce.utwente.nl/rtweb/publications/1999/pdf-files/014_R99.pdf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Communicating Threads for Java</FONT></A>”（IOS Press，1999）这篇论文中描述了 JCSP 库的一个替代品。<BR><BR>
<LI>Peter Welch 教授和其他人合著的论文“<A href="http://www.wotug.org/paperdb/show_pap.php?f=1&amp;id=359" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Using Java for Parallel Computing -- JCSP versus CTJ</FONT></A>” （IOS Press, 2000）比较了 JCSP 和 CTJ。<BR><BR>
<LI>CSP 可以使用 Java 语言实现，也可以使用其他语言实现： <A href="http://www.cs.kent.ac.uk/projects/ofa/c++csp/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>C++CSP</FONT></A> 是针对 C++ 的实现，而 <A href="http://www.quickstone.com/xcsp/jcspnetworkedition/jsdotnet/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>J#.Net</FONT></A> 是针对 .Net 的实现。<BR><BR>
<LI>Occam-pi 是一个语言平台，它打算用 pi-calculus 的移动特性来扩展 occam 语言的 CSP 想法。请从 <A href="http://www.cs.kent.ac.uk/projects/ofa/kroc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>occam-pi homepage</FONT></A> 了解这个前沿研究的更多信息。<BR><BR>
<LI>在学习 occam 时，您可能还想调查 <A href="http://frmb.org/occ21-extensions.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>occam 编程器的各种扩展</FONT></A>。<BR><BR>
<LI>在 developerWorks <A href="http://www.ibm.com/developerworks/cn/java/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 技术专区</FONT></A> 中可以找到 Java 编程各方面的文章。<BR><BR>
<LI>请参阅 <A href="http://devworks.krcinfo.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Developer Bookstore</FONT></A>，以获得技术书籍的完整清单，其中包括数百本 <A href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 相关主题</FONT></A>的书籍。 <BR><BR>
<LI>还请参阅 <A href="http://www.ibm.com/developerworks/cn/views/java/tutorials.jsp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 技术专区教程页</FONT></A>，以获得 <A href="http://www.ibm.com/developerWorks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>developerWorks</FONT></A> 上免费的、以 Java 为重点的教程。<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>Abhijit Belapurkar 拥有位于印度德里市的印度理工学院（IIT）计算机科学的理工学士学位。在过去的 11 年中，他一直工作在分布式应用程序的架构和信息安全领域，他在使用 Java 平台构建 n 层应用程序方面大约有 6 年的工作经验。他目前是作为高级技术架构师在 J2EE 领域工作，服务于印度班加罗尔的 Infosys 科技有限公司。</TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/mstar/aggbug/7584.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-13 08:47 <a href="http://www.blogjava.net/mstar/archive/2005/07/13/7584.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]适用于 Java 程序员的 CSP ，第 2 部分</title><link>http://www.blogjava.net/mstar/archive/2005/07/13/7583.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 13 Jul 2005 00:46:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/13/7583.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/7583.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/13/7583.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/7583.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/7583.html</trackback:ping><description><![CDATA[<A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/">http://www-128.ibm.com/developerworks/cn/java/j-csp2/</A><BR><SPAN class=atitle2>用 JCSP 进行并发编程</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/java/j-csp2/#author1"><NAME><FONT color=#002c99>Abhijit Belapurkar</NAME></FONT></A><BR>高级技术架构师, Infosys Technologies Limited<BR>2005 年 7 月 07 日</P>
<BLOCKQUOTE><ABSTRACT-EXTENDED>在这篇由三部分构成的面向 Java 程序员的通信顺序进程 （CSP）介绍的第二期中，Abhijit Belapurkar 将介绍如何使用基于 Java 的 JCSP 库来编写能够确保没有并发问题（例如争用风险、 死锁、活动锁、资源耗尽）的 Java 应用程序。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>CSP 是对并发对象之间的复杂交互进行建模的范式。使用 CSP 的主要优势之一是：对程序每一阶段所包含对象的行为进行精确地指定和验证。CSP 的理论和实践对于并发设计和编程领域有深远的影响。它是 occam 这样的编程语言的基础，对其他语言（例如 Ada）的设计也有影响。就像在本文 <A href="ttp://www.ibm.com/developerworks/cn/java/j-csp1.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 1 部分</FONT></A> 简要讨论过的，由于适合在 Java 平台上进行安全、优雅的多线程编程，所以 CSP 对 Java 开发人员也是无价的。</P>
<P>在我的这篇由三部分组成的 Java 平台 CSP 编程介绍的第 2 部分中，我把重点放在 CSP 理论和实践上，特别是它在 Java 语言中多线程程序设计的应用。我将从 CSP 理论的概述开始介绍，然后介绍基于 Java 的 JCSP 库实现，JCSP 的核心是 CSP 。</P>
<P><A name=IDAECAX><SPAN class=atitle2>CSP 基础</SPAN></A><BR>CSP 的基本构造是进程和进程之间各种形式的通信。CSP 中的每件事都是进程，甚至（子）进程网络也是进程。但是，在进程之间没有直接交互 —— 所有交互都通过 CSP 的同步对象（例如各级进程订阅的通信通道和事件边界）实现的。</P>
<P>CSP <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">进程</I> 与典型的 Java 对象不同：封装在进程组件中的数据 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">和</I> 操纵数据的算法都是私有的。也就是说，进程没有对外可以调用的方法（除了启动进程必须调用的方法之外），算法只能在进程自己的控制线程内执行。如果把这种方法与 Java 语言中的方法调用进行对比，就可以立即看出 CSP 是如何消除显式锁定的需求的：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=sidebar1><B>不要错过本系列的其余部分!</B></A><BR>“适用于 Java 程序员的 CSP”是对通信顺序进程（Communicating Sequential Processes —— CSP）进行介绍的由三部分组成的一个系列。CSP 是并发编程的一个范式，它承认了并发编程的复杂性，却并没有把复杂性留给开发人员。请参阅本系列的其他部分：<BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><A href="http://www.ibm.com/developerworks/cn/java/j-csp2/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 2 部分：用 JCSP 进行并发编程</FONT></A><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">第 3 部分： JCSP 的高级主题</P></TD></TR></TBODY></TABLE></P>
<P>在 Java 语言中，在对象上调用的方法总是在调用者的线程中运行。但也有一个特殊的控制线程是通过系统中的多个对象进行工作的。对于大部分情况来说，对象没有自己的生命 —— 它们只是在运行线程调用它们的方法时才存在。因此，不同的执行线程可以在同一时间试图调用同一对象的同一方法，就像 <A href="http://www.ibm.com/developerworks/cn/java/j-csp1.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>第 1 部分</FONT></A>所讨论的那样。显然，这种情况在 CSP 中永远不会发生。</P>
<P><A name=IDAHDAX><SPAN class=atitle3>通信通道和进程网络</SPAN></A><BR>进程间通信最简单的机制就是通过通道读写数据。CSP 中基本的通道构造是<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">同步的（synchronous）</I> 和 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">点对点的（point-to-point）</I>；也就是说，它不包含内部缓冲，并且把一个进程连接到另外一个进程。从这个基本通道开始，有可能构建多个阅读器/写入器通道（即一对多、多对一和多对多）。</P>
<P>CSP 中的进程构成了复杂系统的基本构造块 —— 一个进程可以同一个或多个其他进程连接起来（全都设置成并行的），从而构成一个进程网络。可以把这个网络本身想像成一个进程，这个进程还可以递归地与其他进程、它们自己的网络或者其他类似东西组合在一起，形成一个为了最好地解决手上问题而设计的复杂排列的金字塔。</P>
<P>如果单独考虑，那么进程仅仅是一个独立的串行程序，它只与外部 I/O 设备交互。这个程序本身并不需要考虑在 I/O 通道另一端的进程是否存在或对方的性质。</P>
<P>CSP 理论已经在许多基于 Java 的框架中实现了，包括面向 Java 的通信顺序进程（Communicating Sequential Processes for Java，JCSP） 库。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=sidebar2><B>关于 CSP 的更多内容</B></A><BR>本文提供了对 CSP 复杂主题的一般性介绍。如果对于深入理论底层的数学机制有兴趣，那么请参阅 C.A.R. Hoare 的原文章以及他针对这一主题撰写的书。要想获得 CSP 理论的最新发展（这些年已经做了更新），请参阅 Bill Roscoe 撰写的书。要想获得广泛的参考来源，请参考牛津大学计算机实验室和 WoTUG 主页的 CSP 归档。还请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>参考资料</FONT></A>，以获取所有这些参考和更多内容的链接。</P></TD></TR></TBODY></TABLE></P>
<P><A name=IDADEAX><SPAN class=atitle2>JCSP 库</SPAN></A><BR>JCSP 库由英国坎特伯雷市肯特大学的 Peter Welch 教授和 Paul Austin 开发（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>参考资料</FONT></A>）。对于本文余下的大部分内容来说，我会把重点放在 CSP 概念在 JCSP 中的实现方式上。因为 Java 语言没有提供对 CSP 构造的自带支持，所以 JCSP 库内部使用 Java 语言 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实际</I> 支持的、自带的并发构造，例如 <CODE>synchronized</CODE>、<CODE>wait</CODE> 和 <CODE>notify</CODE>。为了帮助您正确地理解 JCSP 的工作方式，我将从这些 Java 构造的角度对 JCSP 库中某些类的内部实现进行了解释。</P>
<P><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">注意，后续章节中的示例基于或来自 JCSP 库的 Javadoc 文档，或者基于可以在 JCSP 主页上得到的演示文稿。</I></P>
<P><A name=IDA5EAX><SPAN class=atitle2>JCSP 中的进程</SPAN></A><BR>在 JCSP 中，进程实际上就是实现了 <CODE>CSProcess</CODE> 接口的类。清单 1 显示了这个接口：</P><A name=code1><B>清单 1. CSProcess 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package jcsp.lang;

public interface CSProcess
{
    public void run();
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意，<CODE>CSProcess</CODE> 接口看起来就像 Java 语言的 <CODE>Runnable</CODE> 接口，而且它也充当着类似的角色。虽然 JCSP 目前是用标准 Java API 实现的，但是并不需要这样，而且在未来可能真的不需要这样。出于这个原因，在 JCSP 中没有直接使用 <CODE>Runnable</CODE> 接口。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=sidebar3><B>验证 JCSP 程序</B></A><BR>Peter Welch 教授和其他人构建了一个正式的 CSP 模型，从而可以用 CSP 术语对多线程 Java 程序进行分析，并验证程序是否会造成导致争用风险、死锁和资源耗尽的 bug。因为 JCSP 库使用模型底部的监视器机制 （即 <CODE>synchronized()</CODE>、<CODE>wait()</CODE>、<CODE>notify()</CODE> 和 <CODE>notifyAll()</CODE>） ，所以基于 JCSP 的应用程序可以用各种软件工程工具进行验证，其中包括一些商业化支持的工具。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>参考资料</FONT></A>，学习关于 FDR2 的内容，这是一个针对基于 CSP 的程序的模型检测工具。</P></TD></TR></TBODY></TABLE></P>
<P>JCSP 定义了两个接口用于从通道读取对象和向通道写入对象。从通道读取对象的接口叫作 <CODE>ChannelInput</CODE> ，它只有一个方法，叫作 <CODE>read()</CODE>。如果进程调用一个实现 <CODE>ChannelInput</CODE> 接口的对象的这个方法，那么进程会阻塞，直到在通道另一端的进程实际向通道写入了一个对象。 一旦在通道上有对象可用，对象就被返回给调用进程。类似地，<CODE>ChannelOutput</CODE> 接口也只有一个方法，叫作 <CODE>write(Object o)</CODE>。如果进程调用 一个实现 <CODE>ChannelOutput</CODE> 接口的对象的这个方法，进程也会阻塞，直到通道接受对象。正如前面提到过的，最简单的通道类型没有缓冲，所以它在另一端（读取）的进程调用 <CODE>read()</CODE> 之前不会接受对象。</P>
<P>从现在开始，我将使用代码示例来演示这些和其他 JCSP 构造如何工作。在清单 2 中，可以看到一个非常简单的进程，它输出 1 到 100 之间的所有偶数：</P><A name=code2><B>清单 2. 生成 1 到 100 之间偶数的进程</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width=500 bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import jcsp.lang.*;

public class SendEvenIntsProcess implements CSProcess 
{
    private ChannelOutput out;

    public SendEvenIntsProcess(ChannelOutput out)
    {
      this.out = out;
    }

    public void run()
    {
      for (int i = 2; i &lt;= 100; i = i + 2)
      {
        out.write (new Integer (i));
      }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>与每一个写进程对应，必须有一个读进程。如果不存在这样的进程，则会造成 <CODE>SendEvenIntsProcess</CODE> 在 <CODE>ChannelOutput</CODE> 对象的 <CODE>out</CODE> 进行第一次写操作之后立即无限期阻塞。清单 3 演示了一个简单的读进程，该进程与清单 2 介绍的写进程对应：</P><A name=code3><B>清单 3. 对应的消费者进程</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import jcsp.lang.*;

public class ReadEvenIntsProcess implements CSProcess
{
    private ChannelInput in;
    public ReadEvenIntsProcess(ChannelInput in)
    {
      this.in = in;
    }

    public void run()
    {
      while (true)
      {
        Integer d = (Integer)in.read();
        System.out.println("Read: " + d.intValue());
      }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=IDAFZFX><SPAN class=atitle2>JCSP 中的通道</SPAN></A><BR>到目前为止，我只有两个独立的进程。下一步就是使用一个用作共享同步机制的公共通道把它们联系在一起，然后从中剔除一个进程。<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">channel</I> 接口是 JCSP 的 <CODE>ChannelInput</CODE> 和 <CODE>ChannelOutput</CODE> 接口的子接口，是读取和写入对象的公共接口。这个接口有许多可能的实现，就像下面描述的一样：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>类 <CODE>One2OneChannel</CODE>，顾名思义，实现了“单一写入器/单一阅读器”类型的通道。<BR><BR>
<LI>类 <CODE>One2AnyChannel</CODE> 实现了“单一写入器/多阅读器”对象通道。（注意，这不是广播机制，实际上，为了从通道读取对象，多个阅读器要进行相互竞争；在指定时间只有一个阅读器能使用通道和写入器进行沟通。）<BR><BR>
<LI>类 <CODE>Any2OneChannel</CODE> 实现了 “多写入器/单一阅读器”对象通道。同上面的情况一样，写入进程彼此竞争使用通道。在指定时间，只有阅读器和众多写入器中的一个在实际使用通道。<BR><BR>
<LI>类 <CODE>Any2AnyChannel</CODE> 实现了“多写入器/多阅读器”对象通道。读取进程彼此竞争使用的通道，写入进程也一样。在指定时间只有一个阅读器和一个写入器在实际使用通道。</LI></UL>
<P>在清单 3 的示例中，我只有一个写入器进程和一个阅读器进程，所以 <CODE>One2OneChannel</CODE> 类就足够了。驱动器程序的示例代码如清单 4 所示：</P><A name=code4><B>清单 4. 驱动器程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import jcsp.lang.*;

public class DriverProgram
{
    public static void main(String[] args)
    {
      One2OneChannel chan = new One2OneChannel();
      new Parallel
      (
        new CSProcess[]
	    {
	      new SendEvenIntsProcess (chan),
	      new ReadEvenIntsProcess (chan)
	    }
      ).run ();
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>正如代码表示的，我首先实例化一个新的 <CODE>One2OneChannel</CODE> 对象，然后把它传递给 <CODE>SendEvenIntsProcess</CODE> 和 <CODE>ReadEventIntsProcess</CODE> 进程的构造函数。这样做是因为 <CODE>One2OneChannel</CODE> 同时实现了两个接口 —— <CODE>ChannelInput</CODE> 和 <CODE>ChannelOutput</CODE>。</P>
<P><A name=IDAA2FX><SPAN class=atitle3>通道内部</SPAN></A><BR>因为通道在 JCSP 中是重要的概念，所以在进行下一步之前，要确定您确实理解了它们的工作方式。正如我在前面提到的，通道在默认情况下是非缓冲的，但是也可以把它们变成缓冲的。实现方式是：通道本身并不处理缓冲特性，而是把这个责任委托给其他类，其他类必须实现叫作 <CODE>ChannelDataStore</CODE> 的接口。JCSP 为这个接口提供了多个内置实现，其中包括以下几个实现：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><CODE>ZeroBuffer</CODE>，对应默认的非缓冲特性。<BR><BR>
<LI><CODE>Buffer</CODE>，为与之相关联的通道提供了一个阻塞的先进先出的缓冲语义。 <BR><BR>
<LI><CODE>InfiniteBuffer</CODE>，也提供先进先出语义，但是如果缓冲为空，那么可以将阅读器阻塞。写入器永远不会阻塞，因为缓冲可以无限扩展，或者至少到了底层内存系统设置的限制为止。</LI></UL>
<P><A name=IDAA3FX><SPAN class=atitle2>通道实战</SPAN></A><BR>考虑一个实际使用的通道示例。当我创建了如清单 4 所示的 <CODE>One2OneChannel</CODE> 实例时，我把它内部的 <CODE>ChannelDatasource</CODE> 设置成 <CODE>ZeroBuffer</CODE> 的一个新实例。<CODE>ZeroBuffer</CODE> 只能保存一个对象（或整数）。它有一个内部状态变量，该变量的起始值为 <CODE>EMPTY</CODE>，只要放进一个对象，该变量的值就变成 <CODE>FULL</CODE> 了。</P>
<P>当 <CODE>SendEvenIntsProcess</CODE> 进程在它的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">out</I> 通道上进行 <CODE>write</CODE> 操作时，会发生什么呢？<CODE>One2OneChannel</CODE> 类的 <CODE>write()</CODE> 方法是一个 <CODE>synchronized()</CODE> 方法。因此，发送方进程运行所在的线程（很快就会看到发送方进程和阅读器进程运行在独立的线程中）就会得到与这个通道实例相关联的监视器锁，并继续处理方法。在该方法中，业务的第一个顺序就是调用内部持有的 <CODE>ZeroBuffer</CODE> 实例的 <CODE>put</CODE> 方法，把对象（或者在这个示例中是整数）写到 <CODE>ZeroBuffer</CODE> 实例。这样就把缓冲的状态变成 <CODE>FULL</CODE>。这时，调用线程调用 <CODE>wait</CODE>，造成线程进入监视器的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">等候集</I>，后面进行的操作是释放监视器锁和阻塞线程。</P>
<P>稍后，阅读器线程调用通道上的 <CODE>read</CODE> 操作（这也是一个同步的方法，所以阅读器线程在继续处理之前必须得到监视器锁）。因为内部缓冲的状态是 <CODE>FULL</CODE>，所以可用数据将被返回，并发出一个 <CODE>notify()</CODE>。<CODE>notify()</CODE> 唤醒发送方线程，然后发送方线程退出监视器等候集，并重新申请监视器锁。</P>
<P>在反过来的场景中，如果阅读器线程调用通道上的 <CODE>read</CODE> 方法时，通道的内部缓冲状态是 <CODE>EMPTY</CODE>，那么阅读器线程就不得不 <CODE>wait</CODE>，在这种情况下，发送方线程要在把数据对象写入内部缓冲之后通知阅读器线程。</P>
<P><A name=IDARAGX><SPAN class=atitle2>Parallel 构造</SPAN></A><BR>在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/#code4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>清单 4</FONT></A> 中，您可能已经注意到驱动器程序引入了一个新类，叫作 <CODE>Parallel</CODE>。<CODE>Parallel</CODE> 类是由 JCSP 以预定义 <CODE>CSProcess</CODE> 的形式提供的，它接受一组独立的 <CODE>CSProcess</CODE> 实例，并“平行地”运行它们 （除了最后一个之外，所有进程都在独立的线程中运行；最后一个进程由 <CODE>Parallel</CODE> 对象在自己的控制线程中运行）。 <CODE>Parallel</CODE> 进程的 <CODE>run</CODE> 方法只有在所有的部件进程终止的时候才终止。所以 <CODE>Parallel</CODE> 进程是一种把多个独立进程组织起来的机制，它用通道（在驱动器程序中示例中）作为“线”把进程连在一起。</P>
<P>了解 <CODE>Parallel</CODE> 构造的另一个途径是说：它可以把小的、简单的组件组合成更高层次的进程。实际上，<CODE>Parallel</CODE> 允许通过迭代把前面迭代中创建的组件与新的组件连接起来，创建出任意复杂程度的完全<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">连接的进程网络</I>。生成的进程网络可以像一个 <CODE>CSProcess</CODE> 对象一样公开和使用。</P>
<P><A name=IDANCGX><SPAN class=atitle2>Parallel 示例</SPAN></A><BR>JCSP 库提供了一组即插即用的组件，不过仅仅是出于教育的目的，正好适合我的目的：进入其中几个的内部实现，可以很好的表现如何在 JCSP 中组合成网络化的并发进程。我用下面的示例进程来表现 JCSP 中 <CODE>Parallel</CODE> 构造的内部工作方式：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><CODE>PlusInt</CODE> 在两个输入流中都接受整数，把整数加在一起，然后把结果输出到输出流。<BR><BR>
<LI><CODE>Delta2Int</CODE> 平行地把到达它的输入流的每个整数广播到它的两个输出通道。<BR><BR>
<LI><CODE>PrefixInt</CODE> 在它的整数输入流之前加上一个（用户配置的）整数。（也就是说，在这个进程的输出通道上有整数可用之前，第一个输出是预先配置的整数。后面的输出才是从输入流得到的整数。）<BR><BR>
<LI><CODE>IntegrateInt</CODE> 是一个用 <CODE>Parallel</CODE> 构造组合了前三个进程的进程。它的功能是输出来自它的输入通道的整数的中间汇总值。</LI></UL>
<P><CODE>IntegrateInt</CODE> 类的 <CODE>run</CODE> 方法如清单 5 所示：</P><A name=code5><B>清单 5. IntegrateInt 进程</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import jcsp.lang.*;

public class IntegrateInt implements CSProcess 
{
  private final ChannelInputInt in;
  private final ChannelOutputInt out;

  public IntegrateInt (ChannelInputInt in, ChannelOutputInt out)
  {
    this.in = in;
    this.out = out;
  }

  public void run()
  {
      One2OneChannelInt a = new One2OneChannelInt ();
      One2OneChannelInt b = new One2OneChannelInt ();
      One2OneChannelInt c = new One2OneChannelInt ();

      new Parallel 
      (
        new CSProcess[]
        {
          new PlusInt (in, c, a),
          new Delta2Int (a, out, b),
          new PrefixInt (0, b, c)
        }
      ).run ();
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意，与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp2/#code4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>请单 4</FONT></A> 中使用的通道相比，这个示例中使用了不同种类的通道。 <CODE>IntegrateInt</CODE> 类使用 <CODE>ChannelInputInt</CODE> 和 <CODE>ChannelOutputInt</CODE> 通道，顾名思义，可以用它们传递 <CODE>int</CODE> 类型的整数。相比之下，清单 4 中的驱动器程序使用了 <CODE>ChannelInput</CODE> 和 <CODE>ChannelOutput</CODE>，它们是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">对象</I> 通道，可以用来在通道中从发送方给接收方发送任意对象。出于这个原因，在清单 4 中传递 <CODE>int</CODE> 值之前，我不得不把 <CODE>int</CODE> 值包装成 <CODE>Integer</CODE> 对象。</P>
<P>在清单 5 中，还需要注意观察什么呢？实际上，<CODE>PrefixInt</CODE> 进程的第一个输出是 0，它是通过 <CODE>PlusInt</CODE> 进程添加到输入通道到达的第一个整数上的。这个结果被写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">a</I>，它构成了 <CODE>Delta2Int</CODE> 进程的输入通道。<CODE>Delta2Int</CODE> 进程把整数结果写到 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">out</I> （进程的整体输出通道）并把它发送到 <CODE>PrefixInt</CODE> 进程。然后 <CODE>PrefixInt</CODE> 进程把整数作为输入发送给 <CODE>PlusInt</CODE> 进程，并添加到流中的第二个整数，如此类推。</P>
<P><CODE>IntegrateInt</CODE> 进程组成的图示如图 1 所示：</P>
<P><A name=fig1><B>图 1. IntegrateInt 进程</B></A><BR><IMG height=227 alt="IntegrateInt 进程" src="http://www-128.ibm.com/developerworks/cn/java/j-csp2/IntegrateInt.gif" width=437 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P><A name=IDALHGX><SPAN class=atitle2>网络中的网络</SPAN></A><BR><CODE>IntegrateInt</CODE> 进程就是这样由三个小进程组成，它本身可以当作一个复合进程来用。JCSP 库提供了一个叫作 <CODE>SquaresInt</CODE> 的进程，顾名思义，它生成一个整数流，整数流是自然数 （1、2、3、4，等等）的平方。这个进程的代码如清单 6 所示：</P><A name=code6><B>清单 6. SquaresInt 进程</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public class SquaresInt implements CSProcess 
{
  private final ChannelOutputInt out;

  public SquaresInt (ChannelOutputInt out)
  {
    this.out = out;
  }

  public void run()
  {
      One2OneChannelInt a = new One2OneChannelInt ();
      One2OneChannelInt b = new One2OneChannelInt ();

      new Parallel 
      (
        new CSProcess[]
        {
          new NumbersInt (a),
          new IntegrateInt (a, b),
          new PairsInt (b, out)
        }
      ).run ();
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>我可以肯定您已经注意到清单 6 显示的两个新进程。<CODE>NumbersInt</CODE> 是一个内置进程，它只是在其输出通道中输出从 0 开始的自然数。<CODE>PairsInt</CODE> 进程则把连续的一对输入值相加并输出结果。这两个新进程和 <CODE>IntegrateInt</CODE> 一起构成了 <CODE>SquaresInt</CODE> 进程，如图 2 中的图表所示：</P>
<P><A name=fig2><B>图 2. SquaresInt 进程</B></A><BR><IMG height=98 alt="SquaresInt 进程" src="http://www-128.ibm.com/developerworks/cn/java/j-csp2/SquaresInt.gif" width=469 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P><A name=IDAGJGX><SPAN class=atitle3>SquaresInt 的工作方式</SPAN></A><BR>在进入下一部分之前，先来考虑 <CODE>SquaresInt</CODE> 进程的内部工作方式。在下面可以看到 <CODE>SquaresInt</CODE> 内部每个通道上的交通流向：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Channel "a":	[0, 1, 2, 3, 4, 5, 6, 7, 8, ...ad infinitum]
Channel "b":	[0, 1, 3, 6, 10, 15, 21, 28, 36, ...ad infinitum]
Channel "out":	[1, 4, 9, 16, 25, 36, 49, 64, 81 ...ad infinitum]
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>您有没有看这样的模式：写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">a</I> 的整数造成它们也被写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">b</I>，因此也写到通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">out</I>？在第一次“滴答”当中，<CODE>NumbersInt</CODE> 进程把整数 0 写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">a</I>。<CODE>IntegrateInt</CODE> 进程也把整数 0 （是当前汇总的值）写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">b</I>。<CODE>PairsInt</CODE> 进程在这次滴答中什么都不产生，因为它需要处理两个输入。在第二次滴答中，<CODE>NumbersInt</CODE> 进程在它的输出通道上写入整数 1。这造成 <CODE>IntegrateInt</CODE> 进程把汇总值修改成 <CODE>0+1=1</CODE>，所以把整数 1 写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">b</I>。</P>
<P>这时, <CODE>PairsInt</CODE> 有了两个整数输入可以处理 —— 整数 0 来自前一次滴答，整数 1 来自当前滴答。它把它们加在一起，并把输出 <CODE>0+1=1</CODE> 写到通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">out</I>。请注意 1 是 1 的平方，所以我们现在可能是在正确的轨道上。继续把示例前进到下一个（第三个）滴答，<CODE>NumbersInt</CODE> 进程把把整数 2 写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">a</I>。这使 <CODE>IntegrateInt</CODE> 进程把汇总值更新为 <CODE>1</CODE> （前一个汇总值） <CODE>+ 2</CODE> （新值） <CODE>= 3</CODE> 并把这个整数写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">b</I>。</P>
<P><CODE>PairsInt</CODE> 进程看到最后两个整数是什么？它们是 <CODE>1</CODE> （在前一次滴答期间） 和 <CODE>3</CODE> （在当前滴答期间）。所以，进程把这两个整数加在一起，并把 <CODE>1+3=4</CODE> 写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">out</I>。您会注意到 4 是 2 的平方，这意味着 <CODE>SquaresInt</CODE> 工作起来就像它应当工作的那样。实际上，应当继续运行这个程序到任意数量的滴答，这样就可以验证写入通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">out</I> 的整数总是在序列中的下一个整数的平方。我在下一节精确地这一操作。</P>
<P><A name=IDA2MGX><SPAN class=atitle3>数学问题</SPAN></A><BR>就在您纳闷的时候，我想解释一下生成平方值的数学基础。假设在 <CODE>NumbersInt</CODE> 进程已经把整数输出到某个 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n-1</I> 的时候，您偷看到了箱子内部。<CODE>IntegrateInt</CODE> 进程最后生成（而且通过共享通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">b</I> 放到 <CODE>PairsInt</CODE> 进程）的中间汇总会是 <CODE>[1+2+3+...+(n-1)] = (n-1)(n-2)/2</CODE>。</P>
<P>在下一次滴答期间，<CODE>NumbersInt</CODE> 会输出 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n</I>，这造成 <CODE>IntegrateInt</CODE> 进程的中间汇总增长为 <CODE>(1+2+3+...+n) = n(n-1)/2</CODE>。然后这个汇总会通过共享通道 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">b</I> 传给 <CODE>PairsInt</CODE> 进程。 <CODE>PairsInt</CODE> 会把这两个数加在一起，生成 <CODE>[(n-1)(n-2)/2 + n(n-1)/2] = [(n-2) + n](n-1)/2 = (2n-2)(n-1)/2 = (n-1)exp2</CODE>。</P>
<P>接下来，<CODE>NumbersInt</CODE> 进程会产生<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">(n+1)</I>。与之对应，<CODE>IntegrateInt</CODE> 进程会把 <CODE>n(n+1)/2</CODE> 送到 <CODE>PairsInt</CODE> 进程。然后 <CODE>PairsInt</CODE> 会生成 <CODE>[n(n-1)/2 + n(n+1)/2] = nexp2</CODE>。针对所有的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n</I> 对这进行通用化，就会按照期望的那样产生全部平方。</P>
<P><A name=IDAUPGX><SPAN class=atitle2>JCSP 中的确定性</SPAN></A><BR>以上示例演示了 CSP 的复合语言 —— 即如何用 <CODE>Parallel</CODE> 构造把细致的无状态的组件组成分层的网络。所有这类相互通信的平行进程的分层网络的卖点就是：它们是完全确定的。在这个上下文环境中 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">确定</I> 意味着什么呢？它意味着这类分层网络的输出只取决于提供给它的输入，而不用考虑网络运行的运行时环境（JVM）的特性。也就是说，进程网络独立于 JVM 的调度策略，也独立于它所分布的多处理器。（我在这里假设的是个单一节点，但是，没有什么固有的东西会防碍把这个讨论引入物理上分布在多个节点上而在进程之间通过线路进行通信的进程网络上。）</P>
<P>确定性会是工具包中的强大工具，因为它可以让您清晰地推断出程序的行为，不必担心运行时环境对它可能产生的影响。同时，确定性不是并发性编程惟一可能的技术或必需的技术。因为下一个（也是最后一个）实例将显示，非确定性在 JSP 中也是同样强大的实用概念。</P>
<P><A name=IDAFQGX><SPAN class=atitle2>JCSP 中的非确定性</SPAN></A><BR>非确定是许多真实的应用程序的因子，在这些应用程序，可见的输出是某个功能或者事件发生的顺序。换句话说，当结果取决于设计的调度，而 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不是</I> 取决于事件时，就是非确定性在并发应用程序中发挥作用的地方了。您将会看到，JCSP 显式地处理这类问题。</P>
<P>例如，假设一个进程对于 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">下面要做什么</I> 有许多备选项，每个备选项都有一个与之关联的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">警卫（guard）</I>，警卫必须处于“就绪（ready）”状态，这样才能让备选项得以考虑。进程可以从可用的备选项（也就是就绪的）中选择一个选项；选择本身可能基于不同的策略，可能是任意选择、最高优先级选择或者公平选择。</P>
<P><A name=IDAVQGX><SPAN class=atitle3>事件选择策略</SPAN></A><BR>在 JCSP 的特定上下文中，提供了一个叫作 <CODE>Guard</CODE> 的抽象类，竞争进程选择的事件必须继续它。进程本身使用另一个预先提供的类，叫作 <CODE>Alternative</CODE>，这些警卫对象必须以对象数组的形式传递给它的构造函数。<CODE>Alternative</CODE> 类为三种事件选择策略提供了方法。</P>
<P><CODE>Alternative</CODE> 类的 <CODE>select()</CODE> 方法对应着 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">任意选择</I> 策略。<CODE>select()</CODE> 方法调用一直受阻塞，直到一个或多个警卫就绪为止（请记住，所有竞争的警卫对于 <CODE>Alternative</CODE> 类来说都是已知的）。其中一个就绪的警卫被随机选中，它的索引（在传递进去的警卫数组中）也被返回。</P>
<P><CODE>priSelect()</CODE> 方法对应着 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">最高优先级</I> 策略。也就是说，如果不止一个警卫就绪，则返回索引值最低的那个；这里面的假设是：在数组中传递给 <CODE>Alternative</CODE> 构造函数的警卫已经按照优先级顺序进行降序排序了。</P>
<P>最后，方法 <CODE>fairSelect</CODE> 是在多个就绪警卫中进行 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">公平</I> 选择：在这个方法的连续调用中，在其他就绪而且可用的警卫没被选中之前，不会有某个就绪的警卫被选中两次。所以，如果警卫的总数是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n</I>，那么在最坏的情况下，就绪的警卫没获得选中的次数不会连续超过 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">n</I> 次。</P>
<P>如果进程不关心如何选择多个就绪警卫，那么任意选择策略最合适；如果进程想保证没有资源耗尽或者最差服务次数，例如在实时系统中，那么任意选择就不太适用了。在前面的情况下，推荐使用 <CODE>fairSelect</CODE> 方法，而在后面的情况下，用 <CODE>priSelect()</CODE> 方法最好。</P>
<P><A name=IDA3SGX><SPAN class=atitle3>警卫类型</SPAN></A><BR>大体来说，JCSP 提供了三类警卫：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><B>通道警卫</B> 总是对应着进程等候从中读取数据的通道。也就是说，只有在通道另一端的进程已经输出数据，而该数据还没有被进程输入的时候，警卫才就绪。<BR><BR>
<LI><B>计时器警卫</B> 总是和设置（绝对）超时对应。也就是说，如果超时，则计时器警卫就会就绪。<BR><BR>
<LI><B>跳过警卫</B> 总是就绪。</LI></UL>
<P>JCSP 中的<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">通道警卫</I> 可以是以下类型：<CODE>AltingChannelInput</CODE>/<CODE>AltingChannelInputInt</CODE>，只要在对应的通道中有了对象或整数数据，则这两个通道将就绪；或者 <CODE>AltingChannelAccept</CODE>，如果在通道中出现不可接受的“CALL”（这一点后面有更多介绍），则通道就会就绪。这些都是抽象类，它们拥有 <CODE>One2One</CODE> 和 <CODE>Any2One</CODE> 类型通道形式的具体实现。JCSP 中的<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">计时器</I> 警卫属于 <CODE>CSTimer</CODE> 类型，而 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">跳过警卫</I> 则是以 <CODE>Skip</CODE> 类的形式提供的。</P>
<P><A name=IDAWUGX><SPAN class=atitle2>运作中的警卫</SPAN></A><BR>我用一个简单的示例，演示如何用 JCSP 警卫实现并发应用程序中的非确定性，借此总结对 JCSP 的介绍。假设您必须开发一个乘法（或者 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">倍增</I>） 设计，读取的整数在输出通道以固定速率到达，可以用某个乘数乘以它们，然后把它们写入其输出通道。设备可以用一个初始乘数开始，但是这个乘数每 5 秒钟自动加倍。</P>
<P>这个故事中介绍的方法是这样的：系统中存在着第二个控制器进程，它能通过专用通道向设备发送 <CODE>suspend operation</CODE> 信号。这使设备中止自身，并把乘数的当前值通过第二个通道发送给控制器。</P>
<P>在中止的时候，设备只应当允许全部进入的整数不经变化地通过它的输出通道。控制器进程 —— 可能在用设备发送给它的乘数做了某些计算中的一种 —— 通过专用通道把一个新乘数发送回设备。（请注意：只要设备处于 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">中止</I> 状态，就会被迫接受这个乘数。）</P>
<P>更新过的乘数插入到设备，并充当设备的唤醒信号。设备现在继续执行它的放大操作，用新更新的乘数乘上输入的整数。计时器这时也重置，所以新的乘数也在 5 秒之后被设置成加倍数值，如此类推。</P>
<P>图 3 中的图表说明了这个放大设备：</P>
<P><A name=fig3><B>图 3. 放大设备</B></A><BR><IMG height=199 alt="ScaleInt 进程" src="http://www-128.ibm.com/developerworks/cn/java/j-csp2/ScaleInt-2.gif" width=513 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P><A name=IDAZVGX><SPAN class=atitle2>ScaleInt 进程</SPAN></A><BR>放大设备的源代码在清单 7 中显示。这个示例中的非确定性是因为：<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">output</I> 的值基于 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">in</I> 和 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">inject</I> 流的值（同时还基于这些值到达的顺序）。</P><A name=code7><B>清单 7. ScaleInt 进程</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import jcsp.lang.*;
import jcsp.plugNplay.ints.*;

public class ScaleInt implements CSProcess
{
  private int s;
  private final ChannelOutputInt out, factor;
  private final AltingChannelInputInt in, suspend, inject;

  public ScaleInt (int s, AltingChannelInputInt suspend, AltingChannelInputInt in, 
    ChannelOutputInt factor, AltingChannelInputInt inject, ChannelOutputInt out)
  {
    this.s = s;
	this.in = in;
	this.out = out;
	this.suspend = suspend;
	this.factor = factor;
	this.inject = inject;
  }

  public void run()
  {
	final long second = 1000;               // Java timings are in millisecs
	final long doubleInterval = 5*second;
	final CSTimer timer = new CSTimer ();

	final Alternative normalAlt = new Alternative (new Guard[] {suspend, timer, in});
	
	final int NORMAL_SUSPEND=0, NORMAL_TIMER=1, NORMAL_IN = 2;

	final Alternative suspendedAlt = new Alternative (new Guard[] {inject, in});
	
	final int SUSPENDED_INJECT=0, SUSPENDED_IN = 1;
	
	long timeout = timer.read () + doubleInterval;
	timer.setAlarm (timeout);

	while (true)
	{
	  switch (normalAlt.priSelect ())
	  {
		case NORMAL_SUSPEND:
		  suspend.read ();              // don't care what's sent
		  factor.write (s);             // reply with the crucial information
		  boolean suspended = true;
		  while (suspended)
		  {
		    switch (suspendedAlt.priSelect ())
			{
			  case SUSPENDED_INJECT:    // this is the resume signal as well
			    s = inject.read ();     // get the new scaling factor
				suspended = false;      // and resume normal operations
				timeout = timer.read () + doubleInterval;
				timer.setAlarm (timeout);
				break;
			  case SUSPENDED_IN:
			    out.write (in.read ());
				break;
			}
		  }
		  break;
		case NORMAL_TIMER:
		  timeout = timer.read () + doubleInterval;
		  timer.setAlarm (timeout);
		  s = s*2;
		  break;
		case NORMAL_IN:
		  out.write (s * in.read ());
		  break;
	  }
    }
  }
}

import jcsp.lang.*;
import jcsp.plugNplay.ints.*;

public class Controller implements CSProcess
{
  private long interval;
  private final ChannelOutputInt suspend, inject;
  private final ChannelInputInt factor;

  public Controller (long interval, ChannelOutputInt suspend, ChannelOutputInt inject, 
    ChannelInputInt factor)
  { 
    this.interval = interval;
    this.suspend = suspend;
    this.inject = inject;
    this.factor = factor;
  }

  public void run ()
  {
	int currFactor = 0;
	final CSTimer tim = new CSTimer ();
	long timeout = tim.read ();
	while (true)
	{
	  timeout += interval;
	  tim.after (timeout);        // blocks until timeout reached
	  suspend.write (0);          // suspend signal (value irrelevant)
	  currFactor = factor.read ();			
	  currFactor ++;              // compute new factor
	  inject.write (currFactor);  // inject new factor
	}
  }
}

import jcsp.lang.*;
import jcsp.plugNplay.ints.*;

public class DriverProgram
{
  public static void main(String args[])
  {
	try
	{
	  final One2OneChannelInt temp = new One2OneChannelInt ();
	  final One2OneChannelInt in = new One2OneChannelInt ();
	  final One2OneChannelInt suspend = new One2OneChannelInt ();
	  final One2OneChannelInt factor = new One2OneChannelInt ();
	  final One2OneChannelInt inject = new One2OneChannelInt ();
	  final One2OneChannelInt out = new One2OneChannelInt ();
		
	  new Parallel
	  (
		new CSProcess[]
		{
		  new NumbersInt (temp),
		  new FixedDelayInt (1000, temp, in),
		  new ScaleInt (2, suspend, in, factor, inject, out),
		  new Controller (6000, suspend, inject, factor),
		  new PrinterInt (out, "--&gt; ", "\n")
		}
	  ).run ();
	}
	catch (Exception e)
	{
		e.printStackTrace();
	}
  }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>上面的类 <CODE>ScaleInt</CODE> 对应着放大设备。正如前面提到的，这个类必须实现 <CODE>CSProcess</CODE> 接口。因为上面的代码演示了许多概念，所以我将逐个讨论它的不同方面。</P>
<P><A name=IDA1WGX><SPAN class=atitle3>两个备选项</SPAN></A><BR>在 <CODE>ScaleInt</CODE> 类中，我们感兴趣的第一个方法是 <CODE>run()</CODE>。在 <CODE>run()</CODE> 方法中，要做的第一件事是创建 <CODE>Alternative</CODE> 类的两个实例，每个都有一组不同的 <CODE>Guard</CODE> 对象。</P>
<P>第一个 <CODE>Alternative</CODE> 实例由变量 <CODE>normalAlt</CODE> 表示，它是为设备正常操作的时候使用的。与之关联的警卫列表如下所示：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><B>suspend</B> 是 <CODE>One2OneChannelInt</CODE> 的实例。正如前面提到过的，<CODE>One2OneChannelInt</CODE> 实现了单一阅读器/写入器整数通道，通道是零缓冲、完全同步的。这是控制器进程向设备发送中止信号的通道。<BR><BR>
<LI><B>timer</B> 是 <CODE>CSTimer</CODE> 的实例，它被设置成每 5 秒触发一次，每次触发时，设备会把乘数的当前值加倍。<BR><BR>
<LI><B>in</B> 是 <CODE>One2OneChannelInt</CODE> 的实例，设备通过它接收输入的整数。</LI></UL>
<P>第二个 <CODE>Alternative</CODE> 实例由 <CODE>suspendedAlt</CODE> 表示，它是供设备在已经被 <CODE>Controller</CODE> 中止的情况下使用的。与之关联的警卫如下如示：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><B>inject</B> 是 <CODE>One2OneChannelInt</CODE> 的实例，由控制器进程使用，用来向设备发送新的乘数（也充当唤醒信号）。<BR><BR>
<LI><B>in</B> 是前面已经看到的 <CODE>One2OneChannelInt</CODE> 相同的实例；设备通过这个通道接收输入整数。</LI></UL>
<P>两个 <CODE>Alternative</CODE> 实例被用在不同的情况下等候警卫就绪，列表的顺序是隐式的优先级顺序。例如，如果 <CODE>normalAlt</CODE> 的 <CODE>suspend</CODE> 和 <CODE>timer</CODE> 警卫恰好同时就绪，那么和 <CODE>suspend</CODE> 警卫对应的事件首先被处理。</P>
<P><A name=IDAX0GX><SPAN class=atitle3>警卫就绪</SPAN></A><BR>下一个我们感兴趣的是在每个警卫就绪的时候，发生了什么。我首先研究 <CODE>normalSelect</CODE>，假设设备操作正常（也就是说，还没有被中止）：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>如果控制器向设备发送了 <I>suspend</I> 信号，那么这个事件以最高优先级得到处理。作为响应，设备把乘数的当前值通过叫作 <CODE>factor</CODE> 的通道发送给控制器。然后将叫作 <CODE>suspended</CODE> 的内部标志设置为 <CODE>true</CODE>，然后进入循环，等候别人发送信号，以继续其操作。在循环内部，设备调用第二个 <CODE>Alternative</CODE> 实例上的 <CODE>priSelect()</CODE> 方法 （<CODE>suspendedAlt</CODE>）。<BR><BR>这个 <CODE>Alternative</CODE> 实例包含两个警卫：第一个表示控制器向设备发送乘数的事件，第二个表示整数到达设备的输入通道。在前一种情况下，设备用从 <CODE>inject</CODE> 通道读取的值来更新乘数（保存在变量 <CODE>s</CODE> 中），并将 <CODE>suspended</CODE> 标志设置回 <CODE>false</CODE> （这样就保证了在下一次迭代时可以退出内部循环），用当前计时器的值作为基值重新设置闹钟。在后一种情况下，设备只是从它的输入通道读取整数，并把整数写入输出通道（也就是说，在设备中止时，不许使用乘数的要求）。<BR><BR>
<LI>具有下一个优先级得到处理的事件是闹钟到期事件。这造成设备把当前乘数加倍，用当前计时器的值作为基值重新设置闹钟，然后返回，继续等候下一个事件。<BR><BR>
<LI>第三个可能是事件是从设备的输入通道接收整数的事件。与之对应的是，设备读取整数，用当前乘数 <CODE>s</CODE> 乘上它，并将结果写入设备的输出通道。</LI></UL>
<P><A name=IDAD3GX><SPAN class=atitle3>Controller 类</SPAN></A><BR>下一个要考虑的类是 <CODE>Controller</CODE> 类。请记住，控制器类的任务是周期性地（大概是基于复杂的计算）向设备进程插入乘数值。在这个示例中，周期基础只是一个计时器，该计时器按照规律的、配置好的间隔到期。每次到期时，控制器就在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">suspend</I> 上写一个 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">0</I>（也就是说，它将中止设备），并在叫作 <CODE>factor</CODE> 的输入通道上读取当前的乘数。</P>
<P>这时，控制器只是把这个值加一，然后通过一对一通道 （叫作 <CODE>inject</CODE>，专门用于为这个目的） 将它插回设备。这就通知设备继续工作的方式，这时计时器被重新设置成在适当间隔后到期。</P>
<P><A name=IDA33GX><SPAN class=atitle3>DriverProgram 类</SPAN></A><BR>最后剩下的类是驱动器类 <CODE>DriverProgram</CODE>。这个类创建适当的通道和 <CODE>CSProcess</CODE> 实例数组。它用 JCSP 提供的类 <CODE>NumbersInt</CODE> 生成一系列自然数，通过<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">temp</I> 通道传递给另一个叫作 <CODE>FixedDelayInt</CODE> 的内置类。顾名思义，<CODE>FixedDelayInt</CODE> 将来自其输入通道的值在固定延迟（在示例代码中，该延迟是 1 秒）之后发送到它的输出通道。</P>
<P>这个自然数的流每隔一秒就被发送到 <CODE>ScaleInt</CODE> 进程的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">in</I> 通道。<CODE>ScaleInt</CODE> 进程的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">out</I> 通道的输出传递给 JCSP 提供的 <CODE>PrinterInt</CODE> 进程，然后该进程再接着把整数值输出到 <CODE>System.out</CODE>。</P>
<P><A name=IDAR5GX><SPAN class=atitle2>第 2 部分的结束语</SPAN></A><BR>在这个由三部分组成的介绍适用于 Java 程序员的 CSP 的系列文章的第 2 部分中，我解释并演示了并发编程中的 CSP 理论。然后是对 CSP 构造的概述，其中介绍了最流行的基于 Java 的 CSP 库 —— JCSP。由于 Java 语言没有对 CSP 构造提供自带的支持，所以 JCSP 库内部采 Java <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">支持</I> 的自带构造，例如 <CODE>synchronized()</CODE>、<CODE>wait()</CODE> 和 <CODE>notify()</CODE>。为了帮助您正确地理解 JCSP 是如何工作的，我从 Java 构造的角度解释了一些 JCSP 类库的内部实现，然后在几个实际示例中演示了它们的用法。 </P>
<P>这里所进行的讨论可以作为本系列最后一篇文章的绝好基础。在最后一篇文章中，我将解释 CSP 和 AOP 的相似性，并简要地对 CSP 解决并发性的方法和新的 <CODE>java.util.concurrent</CODE> 包解决并发性的方法进行比较，还将介绍许多用 JCSP 进行高级同步 的技术。</P>
<P><A name=IDAQAHX><SPAN class=atitle3>致谢</SPAN></A><BR>非常感谢 Peter Welch 教授在我编写这个文章系列期间给予的鼓励。他在百忙之中抽出时间非常细致地审阅了草稿，并提供了许多宝贵的提高系列质量和准确性的建议。文章中如果还存在错误的话，那都是由于我的原因！我在文章中使用的示例基于或来自 JCSP 库的 javadoc 中提供的示例，以及 JCSP Web 站点上提供的 Powerpoint 演示文稿。这两个来源提供了需要探索的大量信息。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www-128.ibm.com/developerworks/java/library/j-csp2/" target=_blank xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>英文原文</FONT></A>。<BR><BR>
<LI>Brian Goetz 编写的由三部分组成的 “<A href="http://www.ibm.com/developerworks/java/library/j-threads1.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Threading lightly</FONT></A>”是解决 Java 平台上同步问题的巧妙而系统的方法。（developerWorks，2001 年 7 月）<BR><BR>
<LI>Allen Holub 撰写的“<A href="http://www-128.ibm.com/developerworks/cn/java/j-king/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>如果我是国王：关于解决 Java编程语言线程问题的建议</FONT></A>” （developerWorks，2000 年 10 月）是一篇启蒙性的、至今仍有意义的、关于 Java 平台多线程编程错误的概述。<BR><BR>
<LI>C.A.R. Hoare 开创性的论文“<A href="http://doi.acm.org/10.1145/359576.359585" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Communicating Sequential Processes</FONT></A>”把通信顺序进程的并行组成作为一种基本的编程结构化方法提了出来（<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Communications of the ACM Archive</I>，1978）。<BR><BR>
<LI>可以免费获得 PDF 格式的 C.A.R. Hoare 撰写的 <A href="http://www.usingcsp.com/cspbook.pdf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>关于 CSP 的书籍</FONT></A>。<BR><BR>
<LI>Bill Roscoe 撰写的 <A href="http://www.amazon.com/exec/obidos/ASIN/0136744095/qid%3D1109670926/sr%3D11-1/ref%3Dsr%5F11%5F1/002-1631025-4649632" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Theory and Practice of Concurrency</FONT></A> （Prentice Hall, 1997） 是最关于并发性和 CS 主题的最新书籍。<BR><BR>
<LI>牛津大学计算机实验室负责的 <A href="http://www.comlab.ox.ac.uk/archive/csp.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>CSP Archive</FONT></A> 是学习更多关于 CSP 内容的好地方，除此之外，还有 <A href="http://www.wotug.org/index.php" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>WoTUG homepage</FONT></A>。<BR><BR>
<LI>Peter Welch 教授和 Jeremy Martin 合著的“<A href="http://www.wotug.org/paperdb/show_pap.php?f=1&amp;id=352" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Formal Analysis of Concurrent Java Systems</FONT></A>” （IOS Press, 2000） 是在 Java 语言中实践 CSP 的良好起点。<BR><BR>
<LI><A href="http://www.cs.kent.ac.uk/projects/ofa/jcsp/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>JCSP homepage</FONT></A> 由英国坎特伯雷市肯特大学负责。<BR><BR>
<LI><A href="http://www.fsel.com/fdr2_download.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>FDR2</FONT></A> （故障偏差求精，Failures-Divergence Refinement） 是面向基于 CSP 的程序的几个商业化模型检测工具之一。<BR><BR>
<LI>CSP 的实现可用于 Java 语言之外的其他语言：<A href="http://www.cs.kent.ac.uk/projects/ofa/c++csp/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>C++CSP</FONT></A> 是针对 C++ 的实现，而 <A href="http://www.quickstone.com/xcsp/jcspnetworkedition/jsdotnet/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>J#.Net</FONT></A> 是针对 .Net 的实现。<BR><BR>
<LI>Occam-pi 是一个语言平台，它期望用 pi-calculus 的移动特性扩展 occam 语言的 CSP 想法。请从 <A href="http://www.cs.kent.ac.uk/projects/ofa/kroc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>occam-pi homepage</FONT></A> 学习这个尖端的研究。<BR><BR>
<LI>在学习 occam 时，您可能还想调查 <A href="http://frmb.org/occ21-extensions.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>occam 编程器的各种扩展</FONT></A>。<BR><BR>
<LI>在 developerWorks <A href="http://www.ibm.com/developerworks/cn/java/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 技术专区</FONT></A> 可以找到 Java 编程各方面的文章。<BR><BR>
<LI>请参阅 <A href="http://devworks.krcinfo.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Developer Bookstore</FONT></A>，以获得技术书籍的完整清单，其中包括数百本 <A href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 相关主题</FONT></A>的书籍。 <BR><BR>
<LI>还请参阅 <A href="http://www.ibm.com/developerworks/cn/views/java/tutorials.jsp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>Java 技术专区教程页</FONT></A>，以获得 <A href="http://www.ibm.com/developerWorks/cn/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#002c99>developerWorks</FONT></A> 上免费的、以 Java 为重点的教程。<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>Abhijit Belapurkar 拥有位于印度德里市的印度理工学院（IIT）计算机科学的理工学士学位。在过去的 11 年中，他一直工作在分布式应用程序的架构和信息安全领域，他在使用 Java 平台构建 n 层应用程序方面也已经有大约 6 年的工作经验。他目前作为高级技术架构师在 J2EE 领域工作，服务于印度班加罗尔的 Infosys 科技有限公司。</TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/mstar/aggbug/7583.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-13 08:46 <a href="http://www.blogjava.net/mstar/archive/2005/07/13/7583.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]适用于 Java 程序员的 CSP，第 1 部分</title><link>http://www.blogjava.net/mstar/archive/2005/07/13/7582.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 13 Jul 2005 00:44:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/13/7582.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/7582.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/13/7582.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/7582.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/7582.html</trackback:ping><description><![CDATA[<SPAN class=atitle2>在 Java 平台上进行多线程编程的缺陷</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/java/j-csp1.html#author1"><NAME>Abhijit Belapurkar</NAME></A><BR>高级技术架构师, Infosys Technologies Limited<BR>2005 年 6 月 29 日</P>
<BLOCKQUOTE><ABSTRACT-EXTENDED>虽然使用 Java 语言进行多线程应用程序编程并不难掌握，但是许多开发人员都在为了正确地应用它们而挣扎。结果，多线程程序要比我们想像的更容易发生细微的错误，这导致一些开发人员为了避免使用多线程而不惜代价，即使在并发和并行能够很明显地产生最好的设计的时候，他们也不采用多线程。在这篇由三部分组成的系列文章中，developerWorks 的定期投稿者 Abhijit Belapurkar 为您铺设了一条有助于克服对多线程编程恐惧、感受它的好处的道路。文章从多线程编程最常见问题的概述开始，这些问题包括：竞争冒险（race hazard）、死锁、活动锁、资源耗尽（resource starvation），等等。 </ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>在 Java 平台上进行多线程编程是件让人望而生畏的事，这一点得到了广泛认可。实际上，一般的理论似乎是：最好把多线程编程留给 Java 高手。Sun Microsystems 通过（在 EJB 规范中—— 请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）把以下内容描述成 EJB 架构的目标之一，也间接地、更深入地讨论了这个观点：</P>
<BLOCKQUOTE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">应用程序开发人员不必理解低级事务和状态管理细节、不必理解多线程、连接池或者其他复杂的低级 API。</BLOCKQUOTE>
<P>如果以这个观念为起点，那么您也就不会惊讶为什么许多 Java 开发人员要避开设计和开发多线程应用程序。但事实上，许多（即使不是大多数）企业级问题使用某种形式的多线程来解决最合适，而 EJB 和类似的框架并不像它们声称的那样，总是最容易的解决方案。</P>
<P>在这篇由三部分组成的系列文章中，我介绍了一个理论，该理论承认了并发编程的复杂性，并且没有试图隐藏这种复杂性，或者使它不那么难于学习和应用。通信顺序进程（Communicating Sequential Processes —— CSP） 是一个精确的并发数学理论，可以用来构建多线程应用程序，确保构建的程序中不出现并发的常见问题，而且更重要的是，这一点<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">能够得到证实</I>。</P>
<P>在介绍 CSP 理论和它基于 Java 语言的实现 —— JCSP 库 —— 之前，我希望能够确定我们有一个共同的讨论框架。我先从 Java 平台的并发编程技术概述开始，接着提供了多线程应用程序开发缺陷的深入概述，这些缺陷包括：竞争冒险、死锁、活动锁和资源耗尽等。最后，通过一些具体的讨论结束本文，这些讨论包括<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">为什么</I> 不能像您喜欢的那样验证多线程 Java 应用程序，并确定现有变通方法中您最偏爱的变通方法。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=sidebar1><B>不要错过连载剩下的部分！</B></A><BR>“面向 Java 程序员的 CSP”是由三部分组成的、介绍通信顺序进程（Communicating Sequential Processes —— CSP）的系列文章中的一篇。CSP 是并发编程的一个范式，它承认了并发编程的复杂性，却并没有把复杂性留给开发人员。请参阅本系列的其他部分：<BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">第 2 部分：用 JCSP 进行并发编程<BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">第 3 部分： JCSP 的高级主题</P></TD></TR></TBODY></TABLE></P>
<P>有了这些基础，就可以完全地体会 JCSP 的好处了。这是 Java 平台多线程编程的一个概念性和实践性的解决方案，我将在第 2 部分对其进行探讨，还将在 第 3 部分 探讨 Java 平台上更高级的 CSP 应用程序。 </P>
<P>本文假设读者对于 Java 语言的并发编程有一般性的了解，虽然我在文章中也提供了关于这一主题的一个概述。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A> 一节，以获得更详细的信息。</P>
<P><A name=IDAEDG2><SPAN class=atitle2>Java 语言的并发编程</SPAN></A><BR>就其自身来说，并发编程是一种技术，提供了操作的同时执行，不论是在单一系统上还是分布在大量系统上。这类操作实际是一些指令顺序，例如单独某个顶级任务的子任务，这类操作能够并行执行，或者是作为线程，或者是作为进程。线程和进程之间的本质区别在于：进程通常是独立的（例如独立的地址空间），所以只能通过系统提供的进程间通信机制进行交互，而线程通常共享单一进程的状态信息，能够直接共享系统资源和内存中的对象。</P>
<P>可以使用下面两种方法之一，通过多个进程来实现并发。第一种方法是在同一个处理器上运行进程，由操作系统处理进程之间的上下文环境切换。（可以理解，这种切换要比同一进程内多线程之间的上下文环境切换更慢。）第二种方法是构建大规模的并行和复杂的分布式系统，在不同的物理处理器上运行多个进程。</P>
<P>从内建支持的角度来说，Java 语言通过线程提供并发编程；每个 JVM 都能支持许多线程同时执行。可以用以下两种方法之一在 Java 语言中创建线程：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><B>继承 <CODE>java.lang.Thread</CODE> 类</B>。在这种情况下，已经重写的子类的 <CODE>run()</CODE> 方法必须包含实现线程运行时行为的代码。要执行这个代码，需要实例化子类对象，然后调用对象的 <CODE>start()</CODE> 方法，这样就可以在内部执行 <CODE>run()</CODE> 方法了。<BR><BR>
<LI><B>创建 <CODE>Runnable</CODE> 接口的定制实现</B>。这个接口只包含一个 <CODE>run()</CODE> 方法，在这个方法中，要放置应用程序代码。要执行这个代码，需要实例化实现类的对象，然后在创建新 <CODE>Thread</CODE> 时，把对象作为构造函数的参数传入。然后调用新创建的线程对象的 <CODE>start()</CODE> 方法，开始执行控制的新线程。</LI></UL>
<P><A name=IDAYEG2><SPAN class=atitle2>线程安全性和同步</SPAN></A><BR>如果 Java 对象中的某个方法能够安全地运行在多线程环境中，那么就称该方法是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">线程安全的</I>。要获得这种安全性，必须有一种机制，通过该机制，运行同一方法的多个线程就能够同步其操作，这样，在访问相同的对象或代码行时，就会只允许一个线程被处理。这种同步要求线程使用叫作 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">信号</I> 的对象彼此进行沟通。</P>
<P>有一种类型的信号叫作 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">互斥信号</I> 或 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">互斥体</I>。顾名思义，这个信号对象的拥有权是互斥的，也就是说，在任意指定时间，只有一个线程能够拥有互斥体。其他想获得所有权的线程会被阻塞，它们必须等待，直到拥有互斥体的线程释放互斥体。如果多个线程按顺序排队等候同一互斥体，那么在当前拥有者释放它的时候，只有一个等候线程能够得到它；其他线程将继续阻塞。</P>
<P>在 1970 年代初，C.A.R. Hoare 和其他人共同开发了一个叫作 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">监视器</I> 的概念（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。 一个 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">监视器</I> 就是一个代码主体，它的访问受到互斥体的保护。任何想执行这个代码的线程，都必须在代码块顶部得到关联的互斥体，然后在底部再释放它。因为在指定时间只有一个线程能够拥有互斥体，所以这就有效地保证了只有拥有它的线程才能执行监视器的代码块。（受保护的代码不需要相邻 —— 例如，Java 语言中的每个对象都有一个与之关联的监视器。）</P>
<P>任何想在 Java 语言中进行线程编程的开发人员，都会立即把上面的内容当成 <CODE>synchronized</CODE> 关键字所带来的效果。可以确保包含在 <CODE>synchronized</CODE> 块中的 Java 代码在指定时间只被一个线程执行。在内部，可以由运行时将 <CODE>synchronized</CODE> 关键字转换成某一种情况：所有的竞争线程都试图获得与它们（指线程）正在操作的对象实例关联的那个（惟一的一个）互斥体。成功得到互斥体的线程将运行代码，然后在退出 <CODE>synchronized</CODE> 块时释放互斥体。</P>
<P><A name=IDAGGG2><SPAN class=atitle3>等候和通知</SPAN></A><BR><CODE>wait</CODE>/<CODE>notify</CODE> 构造在 Java 语言的线程间通信机制中也扮演了重要的角色。基本的想法是：一个线程需要的某个条件可以由另外一个线程促成。这样，条件的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">wait</I> 就可以得到满足。一旦条件为真，那么引发条件的线程就会 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">notify</I> 等候线程苏醒，并从中止的地方继续进行。</P>
<P><CODE>wait</CODE>/<CODE>notify</CODE> 机制要比 <CODE>synchronized</CODE> 机制更难理解和判断。要想判断出使用 <CODE>wait</CODE>/<CODE>notify</CODE> 的方法的行为逻辑，就要求判断出使用它的所有方法的逻辑。一次判断一个方法，把该方法和其他方法隔离开，是对整体系统行为得出错误结论的可靠方式。显然，这样做的复杂性会随着要判断的方法的数量增长而迅速提高。</P>
<P><A name=IDAOHG2><SPAN class=atitle2>线程状态</SPAN></A><BR>我前面提到过，必须调用新创建的线程的 <CODE>start()</CODE> 方法来启动它的执行。但是，仅仅是调用 <CODE>start()</CODE> 方法并不意味着线程会立即开始运行。这个方法只是把线程的状态从 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">new</I> 变成 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">runnable</I>。只有在操作系统真正安排线程执行的时候，线程状态才会变成 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">running</I> （从 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">runnable</I>）。</P>
<P>典型的操作系统支持两种线程模型 —— 协作式和抢占式。在<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">协作式</I> 模型中，每个线程对于自己对 CPU 的控制权要保留多久、什么时候放弃有最终意见。在这个模型中，因为可能存在某个无赖线程占住控制权不放，所以其他线程可能永远无法得到运行。在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">抢占式</I> 模型中，操作系统本身采用基于时钟“滴答”的计时器，基于这个计时器，操作系统可以强制把控制权从一个线程转移到另外一个线程。在这种情况下，决定哪个线程会得到下一次控制权的调度策略就有可能基于各种指标，例如相对优先级、某个线程已经等待执行的时间长短，等等。</P>
<P>如果出于某些原因，处在 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">running</I> 状态的线程需要等候某个资源（例如，等候设备的输入数据到达，或者等候某些条件已经设定的通知），或者在试图获得互斥体的时候被阻塞，因此线程决定睡眠，那么这时它可以进入 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">blocked</I> 状态。当睡眠周期到期、预期输入到达，或者互斥体当前的拥有者将其释放并通知等候线程可以再次夺取互斥体时，阻塞的线程重新进入 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">runnable</I> 状态。</P>
<P>当线程的 <CODE>run()</CODE> 方法完成时（或者正常返回，或者抛出 <CODE>RuntimeException</CODE> 这样的未检测到异常），线程将终止。这时，线程的状态是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">dead</I>。当线程死亡时，就不能通过再次调用它的 <CODE>start()</CODE> 方法来重新启动它，如果那么做，则会抛出 <CODE>InvalidThreadStateException</CODE> 异常。</P>
<P><A name=IDALZ1RB><SPAN class=atitle2>四个常见缺陷</SPAN></A><BR>正如我已经展示过的，Java 语言中的多线程编程是通过语言支持的大量精心设计的构造实现的。另外，还设计了大量设计模式和指导原则，来帮助人们了解这种复杂性带来的许多缺陷。除此之外，多线程编程会很容易地在不经意间把细微的 bug 带进多线程代码，而且更重要的是，这类问题分析和调试起来非常困难。接下来要介绍的是用 Java 语言进行多线程编程时将会遇到（或者可能已经遇到过）的最常见问题的一个列表。</P>
<P><A name=IDASZ1RB><SPAN class=atitle3>争用条件</SPAN></A><BR>据说 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">争用条件</I> 存在于这样的系统中：多个线程之间存在对共享资源的竞争，而胜出者决定系统的行为。Allen Holub 在他撰写的文章 “programming Java threads in the real world” （请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）提供了一个带有这样 bug 的简单的多线程程序示例。在冲突的访问请求之间进行不正确同步的另一个更可怕的后果是 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">数据崩溃</I>，此时，共享的数据结构有一部分由一个线程更新，而另一部分由另一个线程更新。在这种情况下，系统的行为不是按照胜出线程的意图进行，系统根本不按照任何一个线程的意图行动，所以两个线程最后都将以失败告终。</P>
<P><A name=IDAC01RB><SPAN class=atitle3>死锁</SPAN></A><BR><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">死锁</I> 的情况是指：线程由于等候某种条件变成真（例如资源可以使用），但是它等候的条件无法变成真，因为能够让条件变成真的线程在等候第一个线程“做某件事”。这样，两个线程都在等候对方先采取第一步，所以都无法做事。请参阅 Allen Holub 撰写的文章 （请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>），以获得在多线程 Java 代码中如何发生死锁的示例。</P>
<P><A name=IDAP01RB><SPAN class=atitle3>活动锁</SPAN></A><BR><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">活动锁</I> 与 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">死锁</I> 不同，它是在线程实际工作的时候发生的，但这时还没有完成工作。这通常是在两个线程交叉工作的时候发生，所以第一个线程做的工作被另一个线程取消。一个简单的示例就是：每个线程已经拥有了一个对象，同时需要另外一个线程拥有的另外一个对象。可以想像这样的情况：每个线程放下自己拥有的对象，捡起另外一个线程放下的对象。显然，这两个线程会永远都运行在上锁这一步操作上，结果是什么都做不成。（常见的真实示例就是，两个人在狭窄的走廊相遇。每个人都礼貌地让到另一边让对方先行，但却在相同的时间都让到同一边了，所以两个人还都没法通过。这种情况会持续一些时间，然后两个人都从这边闪到那边，结果还是一点进展也没有。）</P>
<P><A name=IDA001RB><SPAN class=atitle3>资源耗尽</SPAN></A><BR><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">资源耗尽</I>，又称为 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">线程耗尽</I>，是 Java 语言的 <CODE>wait</CODE>/<CODE>notify</CODE> 原语无法保证 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">live-ness</I> 的后果。Java 强制这些方法要拥有它们等候或通知的对象的锁。在某个线程上调用的 <CODE>wait()</CODE> 方法在开始等候之前必须释放监视器锁，然后在从方法返回并获得通知之后，必须再次重新获得锁。因此，Java 语言规范（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>） 在锁本身之外，还描述了一套与每个对象相关的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">等候集（wait set）</I>。一旦线程释放了对象上的锁（在 <CODE>wait</CODE> 的调用之后），线程就会放在这个等候集上。</P>
<P>多数 JVM 实现把等候线程放在队列中。所以，如果在通知发生的时候，还有其他线程在等候监视器，那么就会把一个新线程放在队列尾部，而它并不是下一个获得锁的线程。所以，等到被通知线程实际得到监视器的时候，通知该线程的条件可能已经不再为真，所以它不得不再次 <CODE>wait</CODE>。这种情况可能无限持续下去，从而造成运算工作上浪费（因为要反复把该线程放入等候集和从中取出）和线程耗尽。</P>
<P><A name=IDAD21RB><SPAN class=atitle3>贪心哲学家的寓言</SPAN></A><BR>演示这种行为的原型示例是 Peter Welch 教授描述的“聪明人没有鸡肉”（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-csp1.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。在这个场景中考虑的系统是一所由五位哲学家、一位厨师和一个食堂组成的学院。所有的哲学家（除了一位）都要想想（在代码示例中，考虑的时间是 3 秒）之后才去食堂取饭。而“贪心的”哲学家则不想把时间浪费在思考上 —— 相反，他一次又一次地回到食堂，企图拿到鸡肉来吃。</P>
<P>厨师按照一批四份的定量准备鸡肉，每准备好一份，就送到食堂。贪心的哲学家不断地去厨房，但他总是错过食物！事情是这样的：他第一次到的时候，时间太早，厨师还没开火。因此贪心的哲学家只好干等着（通过 <CODE>wait()</CODE> 方法调用）。在开饭的时候（通过 <CODE>notify()</CODE> 方法调用），贪心的哲学家再一次回到食堂排队等候。但是这次，在他前来等候的时候，他的四位同事已经到了，所以他在食堂队列中的位置在他们后面。他的同事把厨房送来的一批四份鸡肉全部拿走了，所以贪心的哲学家又要在一边等着了。 可怜（也可能是公平的） ，他永远处在这个循环之外。</P>
<P><A name=IDAZ21RB><SPAN class=atitle2>验证的问题</SPAN></A><BR>一般来说，很难按照普通的规范对 Java 编程的多线程程序进行验证。同样，开发自动化工具对于常见的并发问题（例如死锁、活动锁和资源耗尽）进行完整而简单的分析也不太容易——特别是在任意 Java 程序中或者在缺乏并发的正式模型的时候。</P>
<P>更糟的是，并发性问题出了名的变化多端、难于跟踪。每个 Java 开发人员都曾经听说过（或者亲自编写过）这样的 Java 程序：经过严格分析，而且正常运行了相当一段时间，没有表现出潜在的死锁。然后突然有一天，问题发生了，结果弄得开发团队经历许多的不眠之夜来试图发现并修补根本原因。</P>
<P>一方面，多线程 Java 程序容易发生的错误非常不明显，有可能在任意什么时候发生。另一方面，完全有可能这些 bug 在程序中从不出现。问题取决于一些不可知的因素。多线程程序的复杂本质，使得人们很难有效地对其进行验证。没有一套现成的规则可以找出多线程代码中的这类问题，也无法确切地证明这些问题不存在，这些导致许多 Java 开发人员完全避开多线程应用程序的设计和开发，即使用并发和并行的方式对系统进行建模会非常棒，他们也不使用多线程。</P>
<P>确实想进行多线程编程的开发人员通常准备好了以下一个或两个解决方案（至少是一部分）：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>长时间艰苦地测试代码，找出所有出现的并发性问题，诚心地希望到应用程序真正运行地时候已经发现并修复了所有这类问题。<BR><BR>
<LI>大量运行设计模式和为多线程编程建立的指导原则。但是，这类指导原则只在整个系统都按照它们的规范设计的时候才有效，没有设计规则能够覆盖所有类型的系统。</LI></UL>
<P>虽然知道的人不多，但是对于编写（然后验证）正确的多线程应用程序这一问题，还有第三个选项。使用称为通信顺序进程（ Communicating Sequential Processes，CSP）的精确的线程同步的数学理论，可以在设计时最好地处理死锁和活动锁之类的问题。CSP 由 C.A.R. Hoare 与 20 世纪 70 年代后期设计，CSP 提供了有效的方法，证明用它的构造和工具构建的系统可以免除并发的常见问题。</P>
<P><A name=IDAK31RB><SPAN class=atitle2>第 1 部分的结束语</SPAN></A><BR>在这份面向 Java 程序员的 CSP 全面介绍的第一部分中，我把重点放在克服多线程应用程序开发常见问题的第一步上，即了解这些问题。我介绍了 Java 平台上目前支持的多线程编程构造，解释了它们的起源，讨论了这类程序可能会有的问题。我还解释了用正式理论在任意的、大型的和复杂的应用程序中清除这些问题（即竞争冒险、死锁、活动锁和资源耗尽）或者证明这些问题不存在的困难。</P>
<P>在 <A href="http://www.ibm.com/developerworks/java/library/j-csp2/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">第 2 部分</A> 中，有了这个基本框架在脑子里，我将介绍 CSP 和它基于 Java 的实现 —— JCSP 库。您会发现，CSP 是一个复杂的数学理论，有大量强大的应用程序 （我会在第 3 部分 将讨论一些更高级的程序），其中包括多线程编程常见问题的解决方法。</P>
<P>要想了解 JCSP 如何把 CSP 的精华提炼成一个好理解的 Java 构造框架，那么请您现在就跳转至 “第 2 部分：用 JCSP 进行并发编程”。</P>
<P><A name=IDAY31RB><SPAN class=atitle3>致谢</SPAN></A><BR>我非常感谢 Peter Welch 教授在我编写这个文章系列期间给予的鼓励。他在百忙之中抽出时间，非常细致地审阅了本文的草稿，并提供了许多宝贵的提高本系列质量和准确性的建议。文章中如果还存在错误，那么都是由于我的原因！我在文章中使用的示例基于或来自 JCSP 库的 javadoc 中提供的示例，以及 JCSP Web 站点上提供的 Powerpoint 演示文稿。这两个来源都提供了大量将探索的信息。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www-128.ibm.com/developerworks/java/library/j-csp1.html" target=_blank xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">英文原文</A>。<BR><BR>
<LI>Brian Goetz 撰写的由三部分组成的 “<A href="http://www.ibm.com/developerworks/java/library/j-threads1.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Threading lightly</A>”系列是解决 Java 平台上同步问题的巧妙而系统的方法。（developerWorks，2001 年 7 月）。<BR><BR>
<LI>Brian 的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java theory and practice</I> 专栏定期介绍多线程编程。 “<A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp1126/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">并发在一定程度上使一切变得简单</A> ”（developerWorks，2002 年 11 月）首次研究了 <CODE>util.concurrent</CODE>，这是一个广泛采用的用于 Java 平台的并发工具包。<BR><BR>
<LI>Allen Holub 编程了大量 Java 平台多线程编程的内容。他撰写的“<A href="http://www.javaworld.com/javaworld/jw-10-1998/jw-10-toolbox.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Programming Java threads in the real world</A>” （<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JavaWorld</I>，1998 年 10 月）提供了线程问题的示例，例如争用条件和死锁。<BR><BR>
<LI>还请参阅 Holub 有名的苛评“<A href="http://www-128.ibm.com/developerworks/cn/java/j-king/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">v</A>“ （developerWorks，2000 年 10 月）。<BR><BR>
<LI>英国坎特伯雷市肯特大学的 Peter Welch 教授设计了“<A href="http://www.cs.ukc.ac.uk/projects/ofa/java-threads/0.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Wot, no chickens?</A>”问题，举例说明 Java 程序中的资源耗尽。<BR><BR>
<LI>C.A.R. Hoare 开创性的论文“<A href="http://doi.acm.org/10.1145/359576.359585" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Communicating Sequential Processes</A>”将通信顺序进程的并行组合作为一种基本的编程构造方法进行介绍（<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Communications of the ACM Archive</I>，1978）。<BR><BR>
<LI>Hoare 撰写的“<A href="http://doi.acm.org/10.1145/355620.361161" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Monitors:?an operating system structuring concept</A>” （<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Communications of the ACM Archive</I>,1974）第一次向全世界介绍了监视器的概念，他采用了非常有表现力的示例，包括单一资源调度器、有边界缓存、闹钟、缓冲池、磁头优化器，以及一个有问题的读取器和写入器版本。<BR><BR>
<LI><A href="http://java.sun.com/products/ejb/docs.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Enterprise JavaBeans</A> 是若干个试图向 Java 程序员隐藏多线程编程复杂性的框架之一。<BR><BR>
<LI>请参阅 <A href="http://java.sun.com/docs/books/jls/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java Language Specification</A>。<BR><BR>
<LI>在 developerWorks 的 <A href="http://www.ibm.com/developerworks/cn/java/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java 技术专区</A> 中，可以找到 Java 编程各方面的文章。<BR><BR>
<LI>请参阅 <A href="http://devworks.krcinfo.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Developer Bookstore</A>，以获得技术书籍的完整清单，其中包括数百本 <A href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java 相关主题</A> 的书籍。 <BR><BR>
<LI>还请参阅 <A href="http://www.ibm.com/developerworks/cn/cviews/java/tutorials.jsp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java 技术专区教程页</A>，从 <A href="http://www.ibm.com/developerWorks/cn/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks</A> 获得免费的、以 Java 为重点的教程。<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>Abhijit Belapurkar 从印度德里市的印度理工学院（IIT）获得了计算机科学方面的理工学士学位。在过去的 11 年中，他一直工作在分布式应用程序的架构和信息安全领域，并在使用 Java 平台构建 N-层应用程序方面拥有大约 6 年的经验。他当前在印度班加罗尔市的 Infosys Technologies Limited 担任 J2EE 方面的高级技术架构师。 </TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/mstar/aggbug/7582.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-13 08:44 <a href="http://www.blogjava.net/mstar/archive/2005/07/13/7582.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Java规则引擎与其API(JSR-94)</title><link>http://www.blogjava.net/mstar/archive/2005/07/13/7581.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 13 Jul 2005 00:27:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/13/7581.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/7581.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/13/7581.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/7581.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/7581.html</trackback:ping><description><![CDATA[<A href="http://www-128.ibm.com/developerworks/cn/java/j-java-rules/index.html?ca=dwcn-newsletter-java">http://www-128.ibm.com/developerworks/cn/java/j-java-rules/index.html?ca=dwcn-newsletter-java</A><BR><SPAN class=atitle>Java规则引擎与其API(JSR-94)<BR>
<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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#IDACCWOB">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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#IDAGDWOB">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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#IDA2EWOB">3、 Java规则引擎</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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#IDAAGWOB">4、 Java规则引擎API(JSR-94)</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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#IDAZHWOB">5、 规则语言</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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#IDAGY1OB">6、 Java规则引擎API使用示例 </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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#IDARZ1OB">7、 结束语 </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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#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 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/java/j-java-rules/index.html?ca=dwcn-newsletter-java#author1"><NAME>李国乐</NAME></A><BR>南京大学计算机系<BR>2005 年 7 月 </P>
<BLOCKQUOTE>本文对Java规则引擎与其API(JSR-94)及相关实现做了较详细的介绍，对其体系结构和API应用有较详尽的描述，并指出Java规则引擎,规则语言，JSR-94的相互关系,以及JSR-94的不足之处和展望</BLOCKQUOTE>
<P>复杂企业级项目的开发以及其中随外部条件不断变化的业务规则(business logic),迫切需要分离商业决策者的商业决策逻辑和应用开发者的技术决策，并把这些商业决策放在中心数据库或其他统一的地方，让它们能在运行时（即商务时间）可以动态地管理和修改从而提供软件系统的柔性和适应性。规则引擎正是应用于上述动态环境中的一种解决方法。</P>
<P>本文第一部分简要介绍了规则引擎的产生背景和基于规则的专家系统，第二部分介绍了什么是规则引擎及其架构和算法，第三部分介绍了商业产品和开源项目实现等各种Java规则引擎，第四部分对Java规则引擎API（JSR-94）作了详细介绍，讲解了其体系结构，管理API和运行时API及相关安全问题，第五部分则对规则语言及其标准化作了探讨，第六部分给出了一个使用Java规则引擎API的简单示例，第七部分给予小结和展望。</P>
<P><A name=IDACCWOB><SPAN class=atitle2>1、 介绍</SPAN></A><BR></P>
<P><A name=IDAHCWOB><SPAN class=atitle3>1.1 规则引擎产生背景</SPAN></A><BR>企业管理者对企业级IT系统的开发有着如下的要求：(1)为提高效率，管理流程必须自动化，即使现代商业规则异常复杂(2)市场要求业务规则经常变化，IT系统必须依据业务规则的变化快速、低成本的更新(3)为了快速、低成本的更新，业务人员应能直接管理IT系统中的规则，不需要程序开发人员参与。</P>
<P>而项目开发人员则碰到了以下问题:(1)程序=算法+数据结构，有些复杂的商业规则很难推导出算法和抽象出数据模型(2)软件工程要求从需求-&gt;设计-&gt;编码，然而业务规则常常在需求阶段可能还没有明确，在设计和编码后还在变化，业务规则往往嵌在系统各处代码中(3)对程序员来说，系统已经维护、更新困难，更不可能让业务人员来管理。</P>
<P>基于规则的专家系统的出现给开发人员以解决问题的契机。规则引擎由基于规则的专家系统中的推理引擎发展而来。下面简要介绍一下基于规则的专家系统。</P>
<P><A name=IDAPCWOB><SPAN class=atitle3>1.2 基于规则的专家系统(RBES)</SPAN></A><BR>专家系统是人工智能的一个分支，它模仿人类的推理方式，使用试探性的方法进行推理，并使用人类能理解的术语解释和证明它的推理结论。专家系统有很多分类：神经网络、基于案例推理和基于规则系统等。</P>
<P>RBES包括三部分：Rule Base（knowledge base）、Working Memory（fact base）和Inference Engine（推理引擎）。它们的结构如下所示：</P>
<P><A name=IDAXCWOB><B>图1.基于规则的专家系统组成</B></A><BR><IMG height=171 alt=图1.基于规则的专家系统组成 src="http://www-128.ibm.com/developerworks/cn/java/j-java-rules/images/image002.jpg" width=440 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>如上图所示，推理引擎包括三部分：Pattern Matcher、Agenda和Execution Engine。Pattern Matcher何时执行哪个规则；Agenda管理PatternMatcher挑选出来的规则的执行次序；Execution Engine负责执行规则和其他动作。</P>
<P>推理引擎通过决定哪些规则满足事实或目标，并授予规则优先级，满足事实或目标的规则被加入议程。存在两者推理方式：演绎法（Forward-Chaining正向链）和归纳法（Backward-Chaining反向链）。演绎法从一个初始的事实出发，不断地应用规则得出结论（或执行指定的动作）。而归纳法则是从假设出发，不断地寻找符合假设的事实。</P>
<P><A name=IDAGDWOB><SPAN class=atitle2>2、 规则引擎</SPAN></A><BR></P>
<P><A name=IDALDWOB><SPAN class=atitle3>2.1 业务规则</SPAN></A><BR>一个业务规则包含一组条件和在此条件下执行的操作，它们表示业务规则应用程序的一段业务逻辑。业务规则通常应该由业务分析人员和策略管理者开发和修改，但有些复杂的业务规则也可以由技术人员使用面向对象的技术语言或脚本来定制。业务规则的理论基础是:设置一个或多个条件，当满足这些条件时会触发一个或多个操作。</P>
<P><A name=IDARDWOB><SPAN class=atitle3>2.2 规则引擎</SPAN></A><BR>什么是规则引擎？规则引擎是如何执行规则的？这可以称之为"什么"与"如何"的问题。到底规则引擎是什么还是目前业界一个比较有争议的问题，在JSR-94种也几乎没有定义。可以这样认为充分定义和解决了"如何"的问题，"什么"问题本质上也迎刃而解。也许这又是一种"先有蛋还是先有鸡"哲学争论。今后标准规则语言的定义和推出及相关标准的制定应该可以给这样的问题和争论划上一个句号。本文中，暂且这样述说什么是规则引擎：规则引擎由推理引擎发展而来，是一种嵌入在应用程序中的组件，实现了将业务决策从应用程序代码中分离出来，并使用预定义的语义模块编写业务决策。接受数据输入，解释业务规则，并根据规则做出业务决策。</P>
<P><A name=IDAXDWOB><SPAN class=atitle3>2.3 规则引擎的使用方式</SPAN></A><BR>由于规则引擎是软件组件，所以只有开发人员才能够通过程序接口的方式来使用和控制它，规则引擎的程序接口至少包含以下几种API：加载和卸载规则集的API；数据操作的API；引擎执行的API。开发人员在程序中使用规则引擎基本遵循以下5个典型的步骤：创建规则引擎对象；向引擎中加载规则集或更换规则集；向引擎提交需要被规则集处理的数据对象集合；命令引擎执行;导出引擎执行结果，从引擎中撤出处理过的数据。使用了规则引擎之后，许多涉及业务逻辑的程序代码基本被这五个典型步骤所取代。 </P>
<P>一个开放的业务规则引擎应该可以"嵌入"在应用程序的任何位置，不同位置的规则引擎可以使用不同的规则集，用于处理不同的数据对象。此外，对使用引擎的数量没有限制。</P>
<P><A name=IDA4DWOB><SPAN class=atitle3>2.4 规则引擎架构与推理</SPAN></A><BR>规则引擎的架构如下图所示：</P>
<P><A name=IDAFEWOB><B>图2. 业务规则引擎架构</B></A><BR><IMG height=329 alt="图2. 业务规则引擎架构" src="http://www-128.ibm.com/developerworks/cn/java/j-java-rules/images/image004.jpg" width=492 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>规则引擎的推理步骤如下：a. 将初始数据（fact）输入至工作内存(Working Memory)。b. 使用Pattern Matcher将规则库(Rules repository)中的规则（rule）和数据（fact）比较。c. 如果执行规则存在冲突（conflict），即同时激活了多个规则，将冲突的规则放入冲突集合。d. 解决冲突，将激活的规则按顺序放入Agenda。e. 执行Agenda中的规则。重复步骤b至e，直到执行完毕Agenda中的所有规则。</P>
<P>任何一个规则引擎都需要很好地解决规则的推理机制和规则条件匹配的效率问题。</P>
<P>当引擎执行时，会根据规则执行队列中的优先顺序逐条执行规则执行实例，由于规则的执行部分可能会改变工作区的数据对象，从而会使队列中的某些规则执行实例因为条件改变而失效，必须从队列中撤销，也可能会激活原来不满足条件的规则，生成新的规则执行实例进入队列。于是就产生了一种"动态"的规则执行链，形成规则的推理机制。这种规则的"链式"反应完全是由工作区中的数据驱动的。 </P>
<P>规则条件匹配的效率决定了引擎的性能，引擎需要迅速测试工作区中的数据对象，从加载的规则集中发现符合条件的规则，生成规则执行实例。1982年美国卡耐基·梅隆大学的Charles L. Forgy发明了一种叫Rete算法，很好地解决了这方面的问题。目前世界顶尖的商用业务规则引擎产品基本上都使用Rete算法。</P>
<P><A name=IDAWEWOB><SPAN class=atitle3>2.5 规则引擎的算法</SPAN></A><BR>大部分规则引擎产品的算法，基本上都来自于Dr. Charles Forgy在1979年提出的RETE算法及其变体，Rete算法是目前效率最高的一个Forward-Chaining推理算法，Drools项目是Rete算法的一个面向对象的Java实现，Rete算法其核心思想是将分离的匹配项根据内容动态构造匹配树，以达到显著降低计算量的效果。详情请见CIS587:The RETE Algorithm，The Rete Algorithm，RETE演算法，《专家系统原理与编程》中第11章等。</P>
<P><A name=IDA2EWOB><SPAN class=atitle2>3、 Java规则引擎</SPAN></A><BR>目前主流的规则引擎组件多是基于Java和C++程序语言环境，已经有多种Java规则引擎商业产品与开源项目的实现，其中有的已经支持JSR94，有的正朝这个方向做出努力，列出如下：</P>
<P><A name=IDACFWOB><SPAN class=atitle3>3.1 Java规则引擎商业产品</SPAN></A><BR>Java规则引擎商业产品主要有（Jess不是开源项目，它可以免费用于学术研究，但用于商业用途则要收费）：</P>
<P><A name=IDAJFWOB><B></B></A><BR><IMG height=291 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-java-rules/images/table1.gif" width=679 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P><A name=IDAVFWOB><SPAN class=atitle3>3.2 Java规则引擎开源项目</SPAN></A><BR>开源项目的实现主要包括：</P>
<P>Drools - Drools规则引擎应用Rete算法的改进形式Rete-II算法。从内部机制上讲，它使用了和Forgy的算法相同的概念和方法，但是增加了可与面向对象语言无缝连接的节点类型。</P>
<P>Mandarax 基于反向推理（归纳法）。能够较容易地实现多个数据源的集成。例如，数据库记录能方便地集成为事实集(facts sets)，reflection用来集成对象模型中的功能。目前不支持JSR 94</P>
<P>OFBiz Rule Engine - 支持归纳法(Backward chaining).最初代码基于Steven John Metsker的"Building Parsers in Java"，不支持JSR 94</P>
<P>JLisa - JLisa是用来构建业务规则的强大框架，它有着扩展了LISP优秀特色的优点,比Clips还要强大.这些特色对于多范例软件的开发是至关重要的.支持JSR 94</P>
<P>其它的开源项目实现有诸如Algernon, TyRuBa, JTP, JEOPS, InfoSapient, RDFExpert, Jena 2, Euler, JLog, Pellet OWL Reasoner, Prova, OpenRules, SweetRules, JShop2等等。</P>
<P><A name=IDAAGWOB><SPAN class=atitle2>4、 Java规则引擎API(JSR-94)</SPAN></A><BR></P>
<P><A name=IDAFGWOB><SPAN class=atitle3>4.1 简介</SPAN></A><BR>过去大部分的规则引擎开发并没有规范化，有其自有的API，这使得其与外部程序交互集成不够灵活。转而使用另外一种产品时往往意味需要重写应用程序逻辑和API调用，代价较大。规则引擎工业中标准的缺乏成为令人关注的重要方面。2003年11月定稿并于2004年8月最终发布的JSR 94（Java规则引擎API）使得Java规则引擎的实现得以标准化。</P>
<P>Java规则引擎API由javax.rules包定义，是访问规则引擎的标准企业级API。Java规则引擎API允许客户程序使用统一的方式和不同厂商的规则引擎产品交互，就像使用JDBC编写独立于厂商访问不同的数据库产品一样。Java规则引擎API包括创建和管理规则集合的机制，在Working Memory中添加，删除和修改对象的机制，以及初始化，重置和执行规则引擎的机制。</P>
<P><A name=IDAMGWOB><SPAN class=atitle3>4.2 简介Java规则引擎API体系结构</SPAN></A><BR>Java规则引擎API分为两个主要部分:运行时客户API(the Runtime client API)和规则管理API(the rules administration API)。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4.2.1规则管理API</B></P>
<P>规则管理API在javax.rules.admin中定义,包括装载规则以及与规则对应的动作(执行集 execution sets)以及实例化规则引擎。规则可以从外部资源中装载,比如说URI,Input streams, XML streams和readers等等.同时管理API提供了注册和取消注册执行集以及对执行集进行维护的机制。使用admin包定义规则有助于对客户访问运行规则进行控制管理,它通过在执行集上定义许可权使得未经授权的用户无法访问受控规则。</P>
<P>管理API使用类RuleServiceProvider来获得规则管理(RuleAdministrator)接口的实例.规则管理接口提供方法注册和取消注册执行集.规则管理器(RuleAdministrator)提供了本地和远程的RuleExecutionSetProvider.在前面已提及,RuleExecutionSetProvider负责创建规则执行集.规则执行集可以从如XML streams, input streams等来源中创建.这些数据来源及其内容经汇集和序列化后传送到远程的运行规则引擎的服务器上.大多数应用程序中,远程规则引擎或远程规则数据来源的情况并不多见.为了避免这些情况中的网络开销,API规定了可以从运行在同一JVM中规则库中读取数据的本地RuleExecutionSetProvider.</P>
<P>规则执行集接口除了拥有能够获得有关规则执行集的方法,还有能够检索在规则执行集中定义的所有规则对象.这使得客户能够知道规则集中的规则对象并且按照自己需要来使用它们。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4.2.2 运行时API</B></P>
<P>运行时API定义在javax.rules包中,为规则引擎用户运行规则获得结果提供了类和方法。运行时客户只能访问那些使用规则管理API注册过的规则，运行时API帮助用户获得规则对话并且在这个对话中执行规则。</P>
<P>运行时API提供了对厂商规则引擎API实现的类似于JDBC的访问方法.规则引擎厂商通过类RuleServiceProvider(类RuleServiceProvider提供了对具体规则引擎实现的运行时和管理API的访问)将其规则引擎实现提供给客户,并获得RuleServiceProvider唯一标识规则引擎的URL.</P>
<P>URL推荐标准用法是使用类似"com.mycompany.myrulesengine.rules.RuleServiceProvider"这样的Internet域名空间,这将有助于访问URL的唯一性.类RuleServiceProvider内部实现了规则管理和运行时访问所需的接口.所有的RuleServiceProvider要想被客户所访问都必须用RuleServiceProviderManager进行注册。注册方式类似于JDBC API的DriverManager和Driver。</P>
<P>运行时接口是运行时API的关键部分.运行时接口提供了用于创建规则会话(RuleSession)的方法,规则会话如前所述是用来运行规则的.运行时API同时也提供了访问在service provider注册过的所有规则执行集(RuleExecutionSets).规则会话接口定义了客户使用的会话的类型,客户根据自己运行规则的方式可以选择使用有状态会话或者无状态会话。</P>
<P>无状态会话的工作方式就像一个无状态会话bean.客户可以发送单个输入对象或一列对象来获得输出对象.当客户需要一个与规则引擎间的专用会话时,有状态会话就很有用.输入的对象通过addObject() 方法可以加入到会话当中.同一个会话当中可以加入多个对象.对话中已有对象可以通过使用updateObject()方法得到更新.只要客户与规则引擎间的会话依然存在,会话中的对象就不会丢失。</P>
<P>RuleExecutionSetMetaData接口提供给客户让其查找规则执行集的元数据(metadata).元数据通过规则会话接口(RuleSession Interface)提供给用户。</P>
<P>使用运行时Runtime API的代码片断如下所示:</P><A name=IDAAHWOB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
RuleServiceProvider ruleProvider = 
RuleServiceProviderManager.getRuleServiceProvider
("com.mycompany.myrulesengine.rules. RuleServiceProvider");   
RuleRuntime ruleRuntime = ruleProvider.getRuleRuntime();
StatelessRuleSession ruleSession = (StatelessRuleSession)  
ruleRuntime.createRuleSession(ruleURL, null, RuleRuntime.STTELESS_SESSION_TYPE);
List inputRules = new ArrayList();
inputRules.add(new String("Rule 1"));
inputRules.add(new Integer(1));
List resultRules = ruleSession.executeRules(inputRules);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=IDAHHWOB><SPAN class=atitle3>4.3 Java规则引擎API安全问题</SPAN></A><BR>规则引擎API将管理API和运行时API加以分开,从而为这些包提供了较好粒度的安全控制.规则引擎API并没有提供明显的安全机制,它可以和J2EE规范中定义的标准安全API联合使用.安全可以由以下机制提供,如Java authentication and authorization service (JAAS),the Java cryptography extension (JCE),Java secure Socket Extension (JSSE),或者其它定制的安全API.JAAS能被用来定义规则执行集的许可权限,从而只有授权用户才能访问。</P>
<P><A name=IDANHWOB><SPAN class=atitle3>4.4 异常与日志</SPAN></A><BR>规则引擎API定义了javax.rules.RuleException作为规则引擎异常层次的根类.所有其它异常都继承于这个根类.规则引擎中定义的异常都是受控制的异常(checked exceptions),所以捕获异常的任务就交给了规则引擎。规则引擎API没有提供明确的日志机制,但是它建议将Java Logging API用于规则引擎API。</P>
<P><A name=IDATHWOB><SPAN class=atitle3>4.5 JSR 94 小结</SPAN></A><BR>JSR 94 为规则引擎提供了公用标准API,仅仅为实现规则管理API和运行时API提供了指导规范,并没有提供规则和动作该如何定义以及该用什么语言定义规则,也没有为规则引擎如何读和评价规则提供技术性指导.JSR 94规范将上述问题留给了规则引擎的厂商.在下一节我将简要介绍一下规则语言。</P>
<P><A name=IDAZHWOB><SPAN class=atitle2>5、 规则语言</SPAN></A><BR>JSR 94中没有涉及用来创建规则和动作的语言.规则语言是规则引擎应用程序的重要组成部分,所有的业务规则都必须用某种语言定义并且存储于规则执行集中,从而规则引擎可以装载和处理他们。</P>
<P>由于没有关于规则如何定义的公用规范,市场上大多数流行的规则引擎都有其自己的规则语言，目前便有许多种规则语言正在应用，因此，当需要将应用移植到其他的Java规则引擎实现时，可能需要变换规则定义，如将Drools私有的DRL规则语言转换成标准的ruleML，Jess规则语言转换成ruleML等。这个工作一般由XSLT转换器来完成。</P>
<P>规则语言的详情这里不作详细介绍，名称及其网址列出如下：</P><PRE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Rule Markup language (RuleML)                        http://www.ruleml.org/
Simple Rule Markup Language (SRML)                    http://xml.coverpages.org/srml.html
Business Rules Markup Language (BRML)                    http://xml.coverpages.org/brml.html
SWRL: A Semantic Web Rule Language Combining OWL and RuleML   http://www.daml.org/2003/11/swrl/</PRE>
<P>多种规则语言的使用使得不同规则引擎实现之间的兼容性成为问题.通用的规则引擎API或许可以减轻不同厂家API之间的问题,但公用规则语言的缺乏将仍然阻碍不同规则引擎实现之间的互操作性.尽管业界在提出公用规则语言上做出了一些努力, 比如说RuleML,SRML的出现,但距离获得绝大部分规则引擎厂商同意的公用标准还有很长的路要走。</P>
<P><A name=IDAGY1OB><SPAN class=atitle2>6、 Java规则引擎API使用示例 </SPAN></A><BR></P>
<P><A name=IDALY1OB><SPAN class=atitle3>6.1 设置规则引擎</SPAN></A><BR>Java规则引擎的管理活动阶段开始于查找一个合适的javax.rules.RuleServiceProvider对象，这个对象是应用程序访问规则引擎的入口。在J2EE环境中，你可能可以通过JNDI获得RuleServiceProvider。否则，你可以使用javax.rules.RuleServiceProviderManager类：</P><A name=IDARY1OB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
    javax.rules.RuleServiceProviderManager class: 
           String implName = "org.jcp.jsr94.ri.RuleServiceProvider";
           Class.forName(implName);
           RuleServiceProvider 
serviceProvider = RuleServiceProviderManager.getRuleServiceProvider(implName);
  </CODE></PRE></TD></TR></TBODY></TABLE>
<P>拥有了RuleServiceProvider对象，你就可以获得一个javax.rules.admin.RuleAdministrator类。从RuleAdministrator类中，你可以得到一个RuleExecutionSetProvider，从类名可以知道，它用于创建javax.rules.RuleExecutionSets对象。RuleExecutionSet基本上是一个装入内存的，准备好执行的规则集合。</P>
<P>包javax.rules.admin包括两个不同的RuleExecutionSetProvider类。RuleExecutionSetProvider类本身包括了从Serializable对象创建RuleExecutionSets的方法，因此在规则引擎位于远程服务器的情况下，仍然可以使用RuleExecutionSetProvider类，构造器的参数可以通过RMI来传递。另一个类是LocalRuleExecutionSetProvider，包含了其他方法，用于从非Serializable资源（如java.io.Reader－本地文件）创建RuleExectionSets。假设拥有了一个RuleServiceProvider对象，你可以从本地文件rules.xml文件创建一个RuleExectionSet对象。如以下的代码所示：</P><A name=IDA0Y1OB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
RuleAdministrator admin = serviceProvider.getRuleAdministrator();
          HashMap properties = new HashMap();
          properties.put("name", "My Rules");
          properties.put("description", "A trivial rulebase");
          FileReader reader = new FileReader("rules.xml");
          RuleExecutionSet ruleSet = null;
          try {
               LocalRuleExecutionSetProvider lresp =
               admin.getLocalRuleExecutionSetProvider(properties);
               ruleSet = lresp.createRuleExecutionSet(reader, properties);
          } finally {
               reader.close();
          }
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>接下来，你可以使用RuleAdministrator注册获得的RuleExecutionSet，并给它分配一个名称。在运行时，你可以用同一个名称创建一个RuleSession；该RuleSession使用了这个命名的RuleExecutionSet。参见下面的用法：admin.registerRuleExecutionSet("rules", ruleSet, properties);</P>
<P><A name=IDACZ1OB><SPAN class=atitle3>6.2 执行规则引擎</SPAN></A><BR>在运行时阶段，你可以参见一个RuleSession对象。RuleSession对象基本上是一个装载了特定规则集合的规则引擎实例。你从RuleServiceProvider得到一个RuleRuntime对象，接下来，从javax.rules.RuleRuntime得到RuleSession对象。</P>
<P>RuleSession分为两类：stateful和stateless。它们具有不同的功能。StatefulRuleSession的Working Memory能够在多个方法调用期间保存状态。你可以在多个方法调用期间在Working Memory中加入多个对象，然后执行引擎，接下来还可以加入更多的对象并再次执行引擎。相反，StatelessRuleSession类是不保存状态的，为了执行它的executeRules方法，你必须为Working Memory提供所有的初始数据，执行规则引擎，得到一个内容列表作为返回值。</P>
<P>下面的例子中，我们创建一个StatefulRuleSession实例，添加两个对象（一个Integer和一个String）到Working Memory，执行规则，然后得到Working Memory中所有的内容，作为java.util.List对象返回。最后，我们调用release方法清理RuleSession：</P><A name=IDAKZ1OB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
RuleRuntime runtime = rsp.getRuleRuntime();
         StatefulRuleSession session = (StatefulRuleSession)
         runtime.createRuleSession("rules", properties,
         RuleRuntime.STATEFUL_SESSION_TYPE);
         session.addObject(new Integer(1));
         session.addObject("A string");
         session.executeRules();
         List results = session.getObjects();
         session.release();
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=IDARZ1OB><SPAN class=atitle2>7、 结束语 </SPAN></A><BR>Java规则引擎API(JSR-94)允许客户程序使用统一的方式和不同厂商的规则引擎产品交互，一定程度上给规则引擎厂商提供了标准化规范。但其几乎没有定义什么是规则引擎，当然也没有深入到规则是如何构建和操纵的，规则调用的效用，规则与Java语言的绑定等方面。并且JSR-94在对J2EE的支持上也不足。规则语言的标准化，JSR-94的进一步的充实深化都有待研究。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>Expert Systems Principles and Programming（专家系统原理与编程） Joesph Giarratano <BR><BR>
<LI>Business Rule Exchange - the Next XML Wave By Margaret Thorpe<BR><BR>
<LI>JSR 94: Java Rule Engine API <A href="http://jcp.org/en/jsr/detail?id=94" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://jcp.org/en/jsr/detail?id=94</A><BR><BR>
<LI>A Closer Look at the Java Rules Engine API (JSR 94) Benoy Jose<BR><BR>
<LI>Java规则引擎的集成 starrynight<BR><BR>
<LI>用规则引擎替换代码 何仁杰 梁冰<BR><BR>
<LI>Implementing a Rule-Driven Service Oriented Architecture Daniel C. Hayes July 2003<BR><BR>
<LI>JSR-94, What Next? Daniel Selman<BR><BR>
<LI><A href="http://www.javarules.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.javarules.org</A><BR><BR>
<LI><A href="http://www.ilog.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.iLog.com</A><BR><BR>
<LI><A href="http://www.blazesoft.co.uk/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.blazesoft.co.uk/</A><BR><BR>
<LI><A href="http://www.yasutech.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.yasutech.com</A><BR><BR>
<LI><A href="http://herzberg.ca.sandia.gov/jess" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://herzberg.ca.sandia.gov/jess</A><BR><BR>
<LI><A href="http://drools.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://drools.org/</A><BR><BR>
<LI><A href="http://www.mandarax.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.mandarax.org/</A><BR><BR>
<LI><A href="http://www.ofbiz.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.ofbiz.org/</A><BR><BR>
<LI><A href="http://www.ofbiz.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://www.ofbiz.org/</A><BR><BR>
<LI><A href="http://jlisa.sourceforge.net/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://jlisa.sourceforge.net/</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>李国乐 曾在电信行业做过Java规则引擎及其API方面的研究应用工作,目前南京大学计算机科学与技术读研.电子邮件:<A href="mailto:liguole666@163.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">liguole666@163.com</A>,<A href="mailto:liguole@vip.sina.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">liguole@vip.sina.com</A>。</TD></TR></TBODY></TABLE></SPAN><img src ="http://www.blogjava.net/mstar/aggbug/7581.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-13 08:27 <a href="http://www.blogjava.net/mstar/archive/2005/07/13/7581.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Exception management and error tracking in J2EE</title><link>http://www.blogjava.net/mstar/archive/2005/07/12/7526.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 12 Jul 2005 01:14:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/12/7526.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/7526.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/12/7526.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/7526.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/7526.html</trackback:ping><description><![CDATA[[ZZ]<A href="http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-exception.html">http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-exception.html</A><BR>The perpetual debate on exception handling in Java can at best be described as a religious war: On one side, you have the proponents of checked exceptions arguing that callers should always deal with error situations arising in code they call. On the other side stand the followers of unchecked exceptions pointing out that checked exceptions clutter code and often can't be handled in immediate clients anyway, so why force it? 
<P>As junior engineers, we first sided with the proselytes of checked exceptions, but over the years, and after many, many catch blocks later, we have gradually converted to the order of the unchecked. Why? We have come to believe in a simple set of rules for dealing with error situations: 
<P>
<OL>
<LI>If it makes sense to handle the exception, do so 
<LI>If you can't handle it, throw it 
<LI>If you can't throw it, wrap it in an unchecked base exception and then throw it</LI></OL>
<P>But what about those exceptions that bubble all the way to the top, you ask? For those, we install a last line of defense to ensure error messages are always logged and the user is properly notified. 
<P>This article presents yet another framework for exception handling, which extends the enterprise-wide application session facility presented in "<A href="http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-exception.html#resources">Create an Application-Wide User Session for J2EE</A>" (<EM>JavaWorld,</EM> March 2005). J2EE applications that use this framework will: 
<P>
<OL>
<LI>Always present meaningful error messages to users 
<LI>Log unhandled error situations once and only once 
<LI>Correlate exceptions with unique request IDs in log files for high-precision debugging 
<LI>Have a powerful, extensible, yet simple strategy for exception handling at all tiers</LI></OL>
<P>To forge the framework, we wield aspect-oriented programming (AOP), design patterns, and code generation using XDoclet. 
<P>You will find all the code in <A href="http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-exception.html#resources">Resources</A> along with a sample J2EE application that uses it. The source constitutes a complete framework named Rampart, which was developed for Copenhagen County, Denmark in the context of J2EE-based electronic healthcare records (EHR) applications. 
<P><FONT size=+1><STRONG>Why do we need common error handling?</STRONG></FONT><BR>During a project's initial state, significant architecture decisions are made: How will software elements interact? Where will session state reside? What communication protocols will be used? And so on. More often than not, however, these decisions do not include error handling. Thus, some variant on the happy-go-lucky strategy is implemented, and, subsequently, every developer arbitrarily decides how errors are declared, categorized, modeled, and handled. As an engineer, you most likely recognize the results of this "strategy": 
<P>
<OL>
<LI><STRONG>Swollen logs:</STRONG> Every catch block contains a log statement, leading to bloated and redundant log entries caused by polluted source code. 
<LI><STRONG>Redundant implementations:</STRONG> The same type of error has different representations, which complicates how it is handled. 
<LI><STRONG>Broken encapsulation:</STRONG> Exceptions from other components are declared as part of the method signature, breaking the clear division between interface and implementation. 
<LI><STRONG>Noncommittal exception declaration:</STRONG> The method signature is generalized to throw <CODE>java.lang.Exception</CODE>. This way, clients are ensured not to have the least clue about the method's error semantics.</LI></OL>
<P>A common excuse for not defining a strategy is that "Java already provides error handling." This is true, but the language also offers facilities for consistently declaring, signaling, propagating, and responding to errors. The language user is responsible for deciding how these services should be used in an actual project. Several decisions must be made, including: 
<P>
<OL>
<LI><STRONG>To check or not to check:</STRONG> Should new exception classes be checked or unchecked? 
<LI><STRONG>Error consumers:</STRONG> Who needs to know when an unhandled error occurs, and who is responsible for logging and notifying operations staff? 
<LI><STRONG>Basic exception hierarchy:</STRONG> What kind of information should an exception carry and what semantics does the exception hierarchy reflect? 
<LI><STRONG>Propagation:</STRONG> Are nonhandled exceptions declared or transformed into other exception classes, and how are they propagated in a distributed environment? 
<LI><STRONG>Interpretation:</STRONG> How are unhandled exceptions turned into human readable, even multilingual messages?</LI></OL>
<P><FONT size=+1><STRONG>Encapsulate rules in a framework, but hurry!</STRONG></FONT><BR>Our recipe for a common error-handling strategy builds on the following shortlist of ingredients: 
<P>
<OL>
<LI><STRONG>Use unchecked exceptions:</STRONG> By using checked exceptions, clients are forced to take a position on errors they can rarely handle. Unchecked exceptions leave the client with a choice. When using third-party libraries, you don't control whether exceptions are modeled as checked or unchecked ones. In this case, you need unchecked wrapper exceptions to carry the checked ones. The biggest tradeoff in using only unchecked exceptions is that you can't force clients to handle them anymore. Yet, when declared as part of the interface, they remain a crucial element of the contract and continue to be part of Javadoc documentation. 
<LI><STRONG>Encapsulate error handling and install a handler on top of each tier:</STRONG> By having a safety net, you can focus on handling only exceptions relevant to business logic. The handler performs the safe touchdown for the remaining exceptions at the specific tier executing standardized steps: logging, system management notification, transformations, etc. 
<LI><STRONG>Model the exception hierarchy using a "simple living" approach:</STRONG> Don't automatically create new exception classes whenever new error types are discovered. Ask yourself if you are simply dealing with a variation of another type and if the client code is likely to explicitly catch it. Remember that exceptions are objects whose attributes can, at least to some extent, model the variation of different situations. Less than a handful of exception classes will most likely prove enough to satisfy a starting point, and only those that are likely to be handled need specialized attributes. 
<LI><STRONG>Give meaningful messages to end users:</STRONG> Unhandled exceptions represent unpredictable events and bugs. Tell this to the user and save the details for the technical staff.</LI></OL>
<P>Although needs and constraints, exception hierarchies, and notification mechanisms will differ across projects, many elements remain constant. So why not go whole hog and implement common policies in a framework? A framework followed by simple rules of usage is the best way to enforce a policy. Executable artifacts talk to developers, and it is easier to preach architectural principles with a jar file and some Javadoc than with whitepapers and slide shows. 
<P>However, you cannot ask the development team to postpone error handling until a policy and complementing framework support is ready. Error handling must already be determined when the first source file is created. A good place to start is by defining the fundamental exception hierarchy.</P>
<P><FONT size=+1><STRONG>A basic exception hierarchy</STRONG></FONT><BR>Our first practical task is to define an exception hierarchy common enough to be used across projects. The base class for our own unchecked exceptions is <CODE>UnrecoverableException</CODE>, a name that, for historical reasons, remains slightly misleading. You might consider a better title for your own hierarchies. 
<P>When you want to get rid of a checked exception, one that, conceivably, clients will always be able to handle, <CODE>WrappedException</CODE> offers a simple, generic transport mechanism: wrap and throw. The <CODE>WrappedException</CODE> keeps the cause as an internal reference, which works well when the classes for the original exception are still available. When this is not the case, use <CODE>SerializableException</CODE>, which resembles <CODE>WrappedException</CODE> except that it can be used when no assumptions are made on available libraries at the client side. 
<P>Although we prefer and recommend unchecked exceptions, you might keep checked exceptions as an option. The <CODE>InstrumentedException</CODE> interface acts as an interface for both checked and unchecked exceptions that follow a certain pattern of attribute implementation. The interface allows exception consumers to consistently inspect the source—whether it inherits from a checked or an unchecked base. 
<P>The class diagram below shows our basic exception hierarchy. 
<P>
<CENTER>
<P>
<TABLE cellPadding=5 border=0>
<TBODY>
<TR>
<TD>
<P>
<CENTER>
<P><IMG height=231 src="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception1.jpg" width=425 vspace=3> 
<P><BR><FONT size=-1><STRONG>
<P>Figure 1. A basic exception hierarchy</STRONG></FONT></CENTER>
<P></P></TD></TR></TBODY></TABLE>
<P></CENTER>
<P>At this point, we have a strategy and a set of exceptions that can be thrown. It is time to build the safety net. 
<P><FONT size=+1><STRONG>The last line of defense</STRONG></FONT><BR>The article "<A href="http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-exception-p2.html#resources">Create an Application-Wide User Session</A>" presented Rampart, a layered architecture consisting of an enterprise information system tier, a business logic tier made from stateless session beans, and a client tier with both Web and standalone J2SE clients. Exceptions can be thrown from all tiers in this architecture, and can either be handled on site or bubble up until they reach the end of the call chain. Then what? Both J2SE and J2EE application servers guard themselves against offensive behavior by catching stray <CODE>Error</CODE>s and <CODE>RuntimeException</CODE>s, and by dumping the stack trace to <CODE>System.out</CODE>, logging it, or performing some other default action. In any case, if a user is presented with any kind of output, mostly likely, it will prove absolutely meaningless, and, worse, the error will probably have disrupted program stability. We must install our own rampart to provide a more sound exception-handling mechanism for this last line of defense. 
<P>Consider Figure 2: 
<P>
<CENTER>
<P>
<TABLE cellPadding=5 border=0>
<TBODY>
<TR>
<TD>
<P>
<CENTER>
<P><IMG height=399 src="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception2.jpg" width=348 vspace=3> 
<P><BR><FONT size=-1><STRONG>
<P>Figure 2. Exception paths in the sample architecture</STRONG></FONT></CENTER>
<P></P></TD></TR></TBODY></TABLE>
<P></CENTER>
<P>Exceptions may occur on the server side in the EJB (Enterprise JavaBeans) tier and in the Web tier, or on the standalone client. In the first case, exceptions stay in the VM from which they originated as they make their way up to the Web tier. This is where we install our top-level exception handler. 
<P>In the standalone case, exceptions eventually reach the rim of the EJB container and travel along the RMI (remote method invocation) connection to the client tier. Care must be taken not to send any exceptions belonging to classes that live only on the server side, e.g., from object-relational mapping frameworks or the like. The EJB exception handler handles this responsibility by using <CODE>SerializableException</CODE> as a vehicle. On the client side, a top-level Swing exception handler catches any stray errors and takes appropriate action. 
<P><FONT size=+1><STRONG>Exception-handler framework</STRONG></FONT><BR>An exception handler in the Rampart framework is a class that implements the <CODE>ExceptionHandler</CODE> interface. This interface's only method takes two arguments: <CODE>Throwable</CODE>, to handle, and the current <CODE>Thread</CODE>. For convenience, the framework contains an implementation, <CODE>ExceptionHandlerBase</CODE>, which tastes <CODE>Throwable</CODE> and delegates handling to dedicated abstract methods for the flavors <CODE>RuntimeException</CODE>, <CODE>Error</CODE>, <CODE>Throwable</CODE>, and the Rampart-specific <CODE>Unrecoverable</CODE>. Subclasses then provide implementations for these methods and handle each situation differently. 
<P>The class diagram below shows the exception-handler hierarchy with its three default exception handlers. 
<P>
<TABLE cellPadding=5 align=center border=0>
<TBODY>
<TR>
<TD>
<P>
<CENTER>
<P><A href="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception3.jpg" target=new_window><IMG height=253 src="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception3-thumb.jpg" width=425 vspace=3></A> 
<P><BR><FONT size=-1><STRONG>
<P>Figure 3. Exception-handler hierarchy. Click on thumbnail to view full-sized image.</STRONG></FONT></CENTER>
<P></P></TD></TR></TBODY></TABLE>
<P>Some among The Order of the Unchecked believe that Sun should have added hooks into all containers in the J2EE architecture on a per-application basis. This would have allowed custom error-handling schemes, security, and more to be gracefully installed, without reliance on vendor-specific schemes and frameworks. Unfortunately, Sun failed to provide such mechanisms in the EJB specification; so, we pull out the AOP hammer from our toolbox and add exception handling as around-aspects. AspectWerkz, our chosen AOP framework, uses the following aspect for that task: 
<P><CODE>
<P><CODE>public class EJBExceptionHandler implements AroundAdvice {<BR><BR>&nbsp;&nbsp; private ExceptionHandler handler;<BR><BR>&nbsp;&nbsp; public EJBExceptionHandler() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>handler = ConfigHelper.getEJBExceptionHandler();</STRONG><BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; public Object invoke(JoinPoint joinPoint) throws Throwable {<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log log = LogFactory.getLog(joinPoint.getEnclosingStaticJoinPoint().getClass().getName());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.debug("EJB Exception Handler bean context aspect!!");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return joinPoint.proceed();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (RuntimeException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>handler.handle(Thread.currentThread(), e);</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Error e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>handler.handle(Thread.currentThread(), e);</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return null;<BR>&nbsp;&nbsp; }<BR>}<BR></CODE>
<P></CODE>
<P>The actual handler is configurable and obtained through the <CODE>ConfigHelper</CODE> class. If a <CODE>RuntimeException</CODE> or <CODE>Error</CODE> is thrown during execution of the bean business logic, the handler will be asked to handle it. 
<P>The <CODE>DefaultEJBExceptionHandler</CODE> serializes the stack trace of any exception not originating from Sun's core packages into a dedicated <CODE>SerializableException</CODE>, which, on the plus side, allows the stack trace of exceptions whose classes don't exist on remote clients to be propagated over anyway. On the downside, the original exception is lost in translation. 
<P>EJB containers faithfully take any <CODE>RuntimeException</CODE> or <CODE>Error</CODE> and wrap it in a <CODE>java.rmi.RemoteException</CODE>, if the client is remote, and in a <CODE>javax.ejb.EJBException</CODE>, otherwise. In the interest of keeping causes precise and stack traces at a minimum, the framework peels off these transport exceptions inside client <CODE>BusinessDelegate</CODE>s and rethrows the original. 
<P>A Rampart <CODE>BusinessDelegate</CODE> class exposes an EJB-agnostic interface to clients, while wrapping local and remote EJB interfaces internally. <CODE>BusinessDelegate</CODE> classes are generated via XDoclet from EJB implementation classes and follow the structure shown in Figure 4's UML diagram: 
<P>
<CENTER>
<P>
<TABLE cellPadding=5 border=0>
<TBODY>
<TR>
<TD>
<P>
<CENTER>
<P><IMG height=228 src="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception4.jpg" width=425 vspace=3> 
<P><BR><FONT size=-1><STRONG>
<P>Figure 4. An example BusinessDelegate</STRONG></FONT></CENTER>
<P></P></TD></TR></TBODY></TABLE>
<P></CENTER>
<P>The <CODE>BusinessDelegate</CODE> class exposes all business methods from the source EJB implementation class and delegates to the <CODE>LocalProxy</CODE> class or the <CODE>RemoteProxy</CODE> class as appropriate. Internally, the two proxies handle EJB-specific exceptions and, hence, shield the calling <CODE>BusinessDelegate</CODE> from implementation details. The code below is a method from some <CODE>LocalProxy</CODE> class: 
<P><CODE>
<P><CODE>public java.lang.String someOtherMethod()&nbsp;&nbsp;{<BR>&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return serviceInterface.someOtherMethod();<BR>&nbsp;&nbsp; } catch (EJBException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>BusinessDelegateUtil.throwActualException(e);</STRONG><BR>&nbsp;&nbsp; }<BR>&nbsp;&nbsp; return null; // Statement is never reached<BR>}<BR></CODE>
<P></CODE>
<P>The <CODE>serviceInterface</CODE> variable represents the EJB local interface. Any <CODE>EJBException</CODE> instances thrown by the container to indicate an unexpected error are caught and handled by the <CODE>BusinessDelegateUtil</CODE> class, in which the following action takes place: 
<P><CODE>
<P><CODE>public static void throwActualException(EJBException e) {<BR>&nbsp;&nbsp; doThrowActualException(e);<BR>}<BR><BR>private static void doThrowActualException(Throwable actual) {<BR>&nbsp;&nbsp; boolean done = false;<BR>&nbsp;&nbsp; while(!done) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(actual instanceof RemoteException) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; actual = ((RemoteException)actual).detail;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else if (actual instanceof EJBException) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; actual = ((EJBException)actual).getCausedByException();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; done = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp; }<BR>&nbsp;&nbsp; if(actual instanceof RuntimeException) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>throw (RuntimeException)actual;</STRONG><BR>&nbsp;&nbsp; } else if (actual instanceof Error) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>throw (Error)actual;</STRONG><BR>&nbsp;&nbsp; }<BR>}<BR></CODE>
<P></CODE>
<P>The actual exception is extracted and rethrown to the top-level client exception handler. When the exception reaches the handler, the stack trace will be that of the original exception from the server containing the actual error. No superfluous client-side trace is added.<BR></P>
<P><FONT size=+1><STRONG>Swing exception handler</STRONG></FONT><BR>The JVM provides a default top-level exception handler for each control thread. When exercised, this handler dumps the stack trace of <CODE>Error</CODE> and <CODE>RuntimeException</CODE> instances onto <CODE>System.err</CODE> and kills the thread! This rather obstructive behavior is quite far from anything a user needs and not elegant from a debugging perspective. We want a mechanism that allows us to notify the user while retaining the stack trace and a unique request ID for later debugging. "<A href="http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-exception-p3.html#resources">Create an Application-Wide User Session for J2EE</A>" describes how such a request ID becomes available at all tiers. 
<P>For J2SE versions up to 1.4, uncaught exceptions in <CODE>Thread</CODE> instances cause the owner <CODE>ThreadGroup</CODE>'s <CODE>uncaughtException()</CODE> method to execute. A simple way to control exception handling in an application, then, is to simply extend the <CODE>ThreadGroup</CODE> class, overwrite the <CODE>uncaughtException()</CODE> method, and ensure all <CODE>Thread</CODE> instances start within an instance of the custom <CODE>ThreadGroup</CODE> class. 
<P>J2SE 5 provides an even sweeter mechanism by allowing the installation of an <CODE>UncaughtExceptionHandler</CODE> implementation on instances of the <CODE>Thread</CODE> class itself. The handler is used as a callback mechanism when uncaught exceptions reach the <CODE>Thread</CODE> instance's run method. We built the framework with J2SE 1.3+ compatibility in mind; hence we used the <CODE>ThreadGroup</CODE> inheritance approach: 
<P><CODE>
<P><CODE>private static class SwingThreadGroup extends ThreadGroup {<BR><BR>&nbsp;&nbsp; private ExceptionHandler handler;<BR><BR>&nbsp;&nbsp; public SwingThreadGroup(ExceptionHandler handler) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super("Swing ThreadGroup");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.handler = handler;<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; public void uncaughtException(Thread t, Throwable e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<STRONG>handler.handle(t, e);</STRONG><BR>&nbsp;&nbsp; }<BR>}<BR></CODE>
<P></CODE>
<P>The <CODE>SwingThreadGroup</CODE> class shown in the code snippet above overrides the <CODE>uncaughtException()</CODE> method and passes the current <CODE>Thread</CODE> instance and the thrown <CODE>Throwable</CODE> to the configured exception handler. 
<P>A little more magic is needed before we can claim control of all stray exceptions in the client tier. For the scheme to work, all threads must be associated with our specialized <CODE>SwingThreadGroup</CODE> instance. This is accomplished by spawning a new master <CODE>Thread</CODE> instance and passing the <CODE>SwingThreadGroup</CODE> instance with a <CODE>Runnable</CODE> implementation, which executes the entire main program. All <CODE>Thread</CODE> instances spawned from the body of this new master <CODE>Thread</CODE> instance automatically join the <CODE>SwingThreadGroup</CODE> instance and, hence, use our new exception handler when unchecked exceptions are thrown. 
<P>
<TABLE cellPadding=5 align=center border=0>
<TBODY>
<TR>
<TD>
<P>
<CENTER>
<P><A href="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception5.jpg" target=new_window><IMG height=316 src="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception5-thumb.jpg" width=425 vspace=3></A> 
<P><BR><FONT size=-1><STRONG>
<P>Figure 5. Swing exception handler class hierarchy. Click on thumbnail to view full-sized image.</STRONG></FONT></CENTER>
<P></P></TD></TR></TBODY></TABLE>
<P>The framework implements this logic in the <CODE>SwingExceptionHandlerController</CODE> convenience class shown in Figure 5. Applications provide an implementation of the <CODE>SwingMain</CODE> interface along with an exception handler to the controller. The controller must then be started, and the old main <CODE>Thread</CODE> can join the new one and wait for termination. The code below shows how the demo application that accompanies this article accomplishes that task. The method <CODE>createAndShowGUI()</CODE> constitutes the actual application body, which initializes Swing components and transfers control to the user. 
<P><CODE>
<P><CODE>public DemoApp() {<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp; SwingExceptionHandlerController.setHandler(new DefaultSwingExceptionHandler());<BR>&nbsp;&nbsp; SwingExceptionHandlerController.setMain(new SwingMain() {<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public Component getParentComponent() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return frame;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createAndShowGUI();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp; });<BR><BR>&nbsp;&nbsp; SwingExceptionHandlerController.start();<BR>&nbsp;&nbsp; SwingExceptionHandlerController.join();<BR>}<BR></CODE>
<P></CODE>
<P>The last line of defense is now in place for the Swing layer, but we still need to provide a meaningful message to the user. The demo application supplies a rather basic implementation that simply displays a dialog box with an internationalized message and the unique request ID as a support ticket. At the same time, the exception is logged via log4j along with the unique request ID. A more sophisticated error handler might send an email, SNMP message, or the like to technical support with the ID in it. The point is that both client and server logs can be filtered on the support ticket ID to allow precise pinpointing of errors on a per-request basis. 
<P>Figure 6 shows merged Swing client and J2EE server logs to provide the precise flow for the request with ID <CODE>1cffeb4:feb53del38:-7ff6</CODE>, where an exception was thrown. Note that the stack trace contains only information from the server side, where the exception was thrown. 
<P>
<TABLE cellPadding=5 align=center border=0>
<TBODY>
<TR>
<TD>
<P>
<CENTER>
<P><A href="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception6.jpg" target=new_window><IMG height=339 src="http://www.javaworld.com/javaworld/jw-07-2005/images/jw-0711-exception6-thumb.jpg" width=425 vspace=3></A> 
<P><BR><FONT size=-1><STRONG>
<P>Figure 6. Log trace from both client and server with request ID and server stack trace. Click on thumbnail to view full-sized image. </STRONG></FONT></CENTER>
<P></P></TD></TR></TBODY></TABLE>
<P>While the infrastructure for adding exception handling to standalone J2SE applications is basic, things are different as we move to Web-based clients. 
<P><FONT size=+1><STRONG>WAR exception handler</STRONG></FONT><BR>Web applications are among the fortunate components in the J2EE world to have their own explicit way of installing exception-handling capabilities. Through the <CODE>web.xml</CODE> deployment descriptor, exceptions and HTTP errors can be mapped to error pages in the shape of servlets or JavaServer Pages (JSP) pages. Consider the following fragment from a sample <CODE>web.xml</CODE> file: 
<P><CODE>
<P><CODE>&lt;servlet&gt;<BR>&nbsp;&nbsp; &lt;servlet-name&gt;ErrorHandlerServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp; &lt;servlet-class&gt;dk.rhos.fw.rampart.util.errorhandling.ErrorHandlerServlet&lt;/servlet-class&gt;<BR>&lt;/servlet&gt;<BR><BR>&lt;servlet-mapping&gt;<BR>&nbsp;&nbsp; &lt;servlet-name&gt;ErrorHandlerServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp; &lt;url-pattern&gt;/errorhandler&lt;/url-pattern&gt;<BR>&lt;/servlet-mapping&gt;<BR><BR>&lt;error-page&gt;<BR>&nbsp;&nbsp; &lt;exception-type&gt;java.lang.Throwable&lt;/exception-type&gt;<BR>&nbsp;&nbsp; &lt;location&gt;/errorhandler&lt;/location&gt;<BR>&lt;/error-page&gt;<BR></CODE>
<P></CODE>
<P>These tags direct all uncaught exceptions to the URL <CODE>/errorhandler</CODE>, which in this case is mapped to the <CODE>ErrorHandlerServlet</CODE> class. The latter is a dedicated servlet whose sole raison d'etre is to act as a bridge between Web components and the exception-handling framework. When an uncaught exception from this Web application reaches the servlet container, a set of parameters with information about the exception will be added to the <CODE>HttpServletRequest</CODE> instance and passed to the <CODE>ErrorHandlerServlet</CODE> class's service method. The fragment below shows the <CODE>service()</CODE> method: 
<P><CODE>
<P><CODE>...<BR>private static final String CONST_EXCEPTION = "javax.servlet.error.exception";<BR>...<BR><BR>protected void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) <BR>&nbsp;&nbsp; throws ServletException, IOException <BR>{<BR><STRONG><BR>&nbsp;&nbsp; Throwable exception = (Throwable)httpServletRequest.getAttribute(CONST_EXCEPTION);<BR><BR>&nbsp;&nbsp; ExceptionHandler handler = ConfigHelper.getWARExceptionHandler();<BR>&nbsp;&nbsp; handler.handle(Thread.currentThread(), exception);<BR><BR>&nbsp;&nbsp; String responsePage = (String)ConfigHelper.getRequestContextFactory().<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getRequestContext().<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAttribute(ExceptionConstants.CONST_RESPONSEPAGE);<BR></STRONG><BR><BR>&nbsp;&nbsp; if(responsePage == null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;responsePage = "/error.jsp";<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; httpServletResponse.setStatus(HttpServletResponse.SC_OK);<BR><BR>&nbsp;&nbsp; RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher(responsePage);<BR>&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dispatcher.include(httpServletRequest, httpServletResponse);<BR>&nbsp;&nbsp; } catch (Exception e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error("Failed to dispatch error to responsePage " + responsePage, e);<BR>&nbsp;&nbsp; }<BR>}<BR></CODE>
<P></CODE>
<P>In the <CODE>service()</CODE> method, first, the actual exception from the <CODE>HttpServletRequest</CODE> instance is retrieved through the key <CODE>javax.servlet.error.exception</CODE>. Next, the exception handler instance is retrieved. From there on, the handler is invoked, and the <CODE>HttpServletRequest</CODE> instance is forwarded to the page specified by the key <CODE>rampart.servlet.exception.responsepage</CODE>. 
<P>The <CODE>DefaultWARExceptionHandler</CODE> class looks up an internationalized text based on the exception message and redirects output response to the JSP <CODE>/error.jsp</CODE>. This page is then free to display the message to the user, including the current request ID as a support ticket. A much more sophisticated mechanism can easily be implemented by simply extending or replacing this handler. 
<P><FONT size=+1><STRONG>Wrap up</STRONG></FONT><BR>Often, exception handling is not handled stringently enough, thereby complicating debugging and error tracking, and, many times, disrupting the overall user experience. It is therefore crucial to have policies and frameworks in place before system development starts. Adding this aspect as an afterthought is doable, but time consuming and expensive. 
<P>This article has armed you with a starting point for defining an exception policy and introduced you to a simple, yet extensible, unchecked exception hierarchy. We have walked through business and client tiers of a sample J2EE architecture and shown how you can install top-level exception handlers for each tier to provide a last line of defense. The framework code has given you a way to precisely pinpoint errors on a per-user request basis through the unique request ID attached to exceptions and log entries. 
<P>So download the <A href="http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-exception-p3.html#resources">framework</A>, try it out, modify it to your heart's delight, and get those exceptions under control. <IMG height=12 src="http://www.devworld.com/microsites/javaworld/javaworld/icons/jw-dingbat.gif" width=22></P>
<P><BR></P><img src ="http://www.blogjava.net/mstar/aggbug/7526.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-12 09:14 <a href="http://www.blogjava.net/mstar/archive/2005/07/12/7526.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>今天发现了HSQL，觉得这是解决NetCatcher中维持在线状态统计的一个办法</title><link>http://www.blogjava.net/mstar/archive/2005/06/20/6363.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Mon, 20 Jun 2005 01:53:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/20/6363.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/6363.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/20/6363.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/6363.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/6363.html</trackback:ping><description><![CDATA[<P>其实我们要的是内存数据库引擎，就是暴露给我们的是JDBC的API，我们就像操作数据库一样的操作，但是数据并没有写入磁盘，而是在内存中。当然需要持久化时，你也可以把它写入磁盘。<BR>HSQLDB是这样一个实现，它是一个数据库引擎，可以作为嵌入式的数据库，也可以作为内存数据库。并且在很多知名的项目中已经有了应用比如openOffice。<BR>我们应该关注一下！<BR><!--StartFragment --><A href="http://hsqldb.sourceforge.net/">http://hsqldb.sourceforge.net/</A></P>
<P align=left>HSQLDB is the leading SQL relational database engine written in Java. It has a JDBC driver and supports a <A href="http://hsqldb.sourceforge.net/doc/guide/ch08.html">rich subset</A> of <A href="http://www.contrib.andrew.cmu.edu/%7Eshadow/sql/sql1992.txt">ANSI-92 SQL</A> (<A href="http://www.contrib.andrew.cmu.edu/%7Eshadow/sql/sql2bnf.aug92.txt">BNF tree format</A>) plus SQL 99 and 2003 enhancements. It offers a small (less than 100k in one version), fast database engine which offers both in-memory and disk-based tables. Embedded and server modes are available. Additionally, it includes tools such as a minimal web server, in-memory query and management tools (can be run as applets) and a number of demonstration examples.</P>
<P align=left>The product is currently being used as a database and persistence engine in many Open Source Software projects and even in commercial projects and products. In it's current version it is extremely stable and reliable. It is best known for its small size, ability to execute completely in memory and its speed.</P>
<P align=left>This feature-packed software is completely free under <A href="http://hsqldb.sourceforge.net/web/hsqlLicense.html">our licenses </A>, based on the standard BSD license. Yes, that's right, completely free of cost or onerous restrictions and fully compatible with all major open source licenses. Java source code and extensive documentation always included!</P>
<P align=left>Users please visit the <A href="http://sourceforge.net/projects/hsqldb/">Project Summary Page</A> for more information and to submit <A href="http://sourceforge.net/tracker/?group_id=23316&amp;atid=378133">Patches</A>, <A href="http://sourceforge.net/tracker/?group_id=23316&amp;atid=378131">Bugs</A>, <A href="http://sourceforge.net/tracker/?atid=378134&amp;group_id=23316&amp;func=browse">Feature Requests</A> and <A href="http://hsqldb.sourceforge.net/web/hsqlDevelopment.html">communicate with the Project Team</A> or <A href="http://sourceforge.net/mail/?group_id=23316">view/browse/subscribe to one or more of our mailing lists</A>. </P>
<P>Also visit our pages featuring <A href="http://hsqldb.sourceforge.net/doc/zaurus/index.html">HSQLDB for the Sharp Zaurus platform</A>, a special version with a data entry and view interface.</P>
<P>Developers please visit the <A href="http://hsqldb.sourceforge.net/web/hsqlDevelopment.html">Developers Page</A> and join our effort!</P>
<P align=left>Our group was formed in 2001 and has as its charter the continuation of Thomas Mueller's closed Hypersonic SQL Project. We have actively developed and released six new versions of the database since April 2001. The latest release version features a wide range of new functionality and code rewrite. The project enjoys an overall ranking of 82 among all SourceForege projects (85,000) with over <A href="http://sourceforge.net/project/stats/index.php?report=months&amp;group_id=23316">380,000 downloads.</A></P>
<P></P>
<P>The last stable code from Thomas' project (Hypersonic SQL version 1.43) is also available for download at <A href="http://hsql.sourceforge.net/">Thomas Mueller's Hypersonic SQL page at SourceForge</A>.</P><img src ="http://www.blogjava.net/mstar/aggbug/6363.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-20 09:53 <a href="http://www.blogjava.net/mstar/archive/2005/06/20/6363.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cloudscape Version 10: A technical overview</title><link>http://www.blogjava.net/mstar/archive/2005/06/09/5812.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 09 Jun 2005 05:23:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/09/5812.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5812.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/09/5812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5812.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5812.html</trackback:ping><description><![CDATA[
		<!--StartFragment -->  
<p>13 Aug 2004<br />Updated 03 Aug 2004</p><blockquote><abstract-extended>The core of IBM® Cloudscape™ is the Apache Derby open source database. The Derby lightweight, pure Java™ technology-based embeddable architecture makes it the ideal database engine for deploying database-driven Java applications. With Derby, the database engine becomes part of the application so the user never has to install or manage it. This article highlights the Derby embeddable SQL database engine for Java technology and provides you with an overview of Derby technical features.</abstract-extended></blockquote><p></p><table cellspacing="0" cellpadding="5" width="60%" align="center" border="1"><tbody><tr><td background="/developerworks/i/bg-gold.gif"><p><a name="N10060"><span class="atitle2">Technical resources for IBM Cloudscape</span></a><br />If you're looking for resources to begin developing for IBM Cloudscape, a great place to start is the <a href="http://www-106.ibm.com/developerworks/db2/zones/cloudscape/">Cloudscape technical resource center</a> on developerWorks. You'll find links to how-to articles, frequently asked questions, an online discussion forum, documentation and more.</p></td></tr></tbody></table><p><a name="N10070"><span class="atitle2">Prelude</span></a><br />Cloudscape is the original zero admin, embeddable, all java, relational database that entered the marketplace in 1996. In August 2004 <a href="http://www-1.ibm.com/press/PressServletForm.wss?MenuChoice=pressreleases&amp;TemplateName=ShowPressReleaseTemplate&amp;SelectString=t1.docunid=7254&amp;TableName=DataheadApplicationClass&amp;SESSIONKEY=any&amp;WindowTitle=Press+Release&amp;STATUS=publish">IBM contributed Derby,</a> a copy of its Cloudscape 10.0 relational database product, to the Apache Software Foundation (ASF) to help accelerate innovation around data-driven Java applications. IBM continues its Cloudscape commercial offering, which adds features to the core Derby engine. </p><p>Not surprisingly, the existence of two names side-by-side, Cloudscape and Derby, might cause some confusion. With that in mind, this section clarifies what is in the Apache Derby software and what is in the IBM Cloudscape product. We mention features you probably won't recognize yet; but, rest assured, we describe them later in this technical article. </p><p>This technical article uses "Derby" when referring to any feature that is part of the open source database engine, including: </p><ul><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_rdbms">The core RDBMS engine</a>. 
</li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_jdbc">The embedded JDBC Driver</a>. 
</li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_cliserv">The Network Server</a>.</li></ul><p>The IBM Cloudscape product includes Derby without any modification whatsoever to the underlying source code. This technical article uses "Cloudscape" when referring to the features IBM adds to the core Derby engine, such as: </p><ul><li>IBM Technical Support. 
</li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_jdbc">IBM DB2 UDB Universal JDBC Driver</a> for use with the Derby <a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_cliserv">Network Server</a>. 
</li><li>The IBM Cloudscape manuals and <a href="http://publib.boulder.ibm.com/infocenter/cldscp10/index.jsp">Cloudscape Information Center</a>. 
</li><li>Installers and sample databases that jump start developers who are new to Java and/or Derby. </li></ul><p>At times, you might notice Derby and Cloudscape being used as synonyms, and you might see the core database engine referred to as Cloudscape, especially in other IBM developerWorks technical articles and when referring to older Cloudscape releases. We apologize for any confusion. Moving forward, think --Derby-- for the core database features and --IBM Cloudscape-- for the supported commercial release. And if you find yourself using the two terms synonymously, don't worry; we understand what you mean. </p><p><a name="N100BC"><span class="atitle2">Introduction</span></a><br />Derby is a lightweight, embeddable relational engine in the form of a Java class library. Its native interface is Java Database Connectivity (JDBC), with Java-relational extensions. It implements the SQL92E standard as well as many SQL 99 extensions. The engine provides transactions and crash recovery, and allows multiple connections and multiple threads to use a connection. Derby can be easily embedded into any Java application program or server framework without compromising the Java-ness of the application because it is a Java class library. Derby's support for complex SQL transactions and JDBC allows your applications to migrate to other SQL databases, such as IBM DB2® Universal Database™ (UDB), when they need to grow.</p><p>The Derby Network Server provides multi-user connectivity to Derby databases within a single system or over a network. The Derby Network Server receives and replies to queries from clients using standard network protocol. Databases are accessed through the Derby Network Server using the IBM DB2 Universal JDBC Driver. (Not all Derby functionality is supported when using the Network Server. See the <b>Server and Administration Guide</b> for details.)</p><p>There are several technical aspects that differentiate Derby from other database systems: </p><ul><li>Derby is easy to administer. When embedded in a client application, a Derby system requires no administrative intervention. 
</li><li>Derby is embeddable. Applications can embed the Database Management System (DBMS) engine in the application process, eliminating the need to manage a separate database process or service. 
</li><li>Derby can run as a separate process, using the Network Server framework or a server framework of your choice. 
</li><li>Derby is a pure Java class library: This is important to Java developers who are trying to maintain the advantages of Java technology, such as platform independence, ease of configuration, and ease of installation. 
</li><li>Derby needs no proprietary Java Virtual Machine (JVM). Written entirely in the Java language, it runs with any certified JVM. 
</li><li>The Derby DBMS engine is lightweight. It is about 2MB of class files, and it uses as little as 4MB of Java heap. 
</li><li>Derby provides the ability to write stored procedures and functions in Java that can run in any tier of an application. Derby does not have a proprietary stored procedure language; it uses JDBC.</li></ul><p>Derby is also like other relational database systems. Derby implements the SQL92E language standard and the JDBC API standard. It has transactions (commit and rollback), supports multiple connections with transactional isolation, and provides crash recovery. Like other databases, it allows multiple threads to share the same connection, and Derby implements many SQL99 features, with extensions for Java technology.</p><p>This unique combination of technical capabilities allows application developers to build data-driven applications that are pervasive (run anywhere), deployable (downloadable), manageable, extensible, and connectable. These technical features are discussed in this article, organized as shown below:</p><ul><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_arch">General architecture</a><ul><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_embed">Embedded</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_cliserv">Network Server</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#integration">Easy Integration</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#persistence">Data Persistence</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#flexibility">Flexibility</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_api">Programmer's application program interface (API)</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_jdbc">JDBC drivers</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_odbc">CLI/ODBC driver</a></li></ul></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_rdbms">Relational Database Management System (RDBMS) capabilities</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_security">Security features</a><ul><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_signed_jar">Signed Java class file archive (JAR) files</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_encrypt">Encrypted databases</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_auth">External user authentication</a></li></ul></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_extensions">Java extensions</a><ul><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_func">Java functions</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_trigger">Using Java functions in triggers</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_sp">Java stored procedures</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_load">Loading Java classes from the database</a></li></ul></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#value_add">IBM Cloudscape Features</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_summary">Summary</a></li></ul><p><a name="cs_arch"><span class="atitle2">General architecture</span></a><br />Derby is a relational database management engine implemented as a Java class library. Applications access data managed by the Derby engine with the JDBC API.</p><p><a name="cs_embed"><span class="atitle3">Embedded</span></a><br />The database engine is embeddable. This means that rather than running as a separate process, the database engine software can be part of the application so that the application and the database engine run in the same JVM. When embedded, the application uses the JDBC API to access the database. The embedded JDBC driver transfers data to and from the database engine without the need for network communication. Whether or not it is embedded, the database engine supports multiple simultaneous connections and access from multiple application threads.</p><p><a name="N10198"><b>Figure 1. Derby embedded in an application</b></a><br /><img height="180" alt="" src="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/fig1.gif" width="513" /></p><p><a name="cs_cliserv"><span class="atitle3">Network Server</span></a><br />It is also possible to deploy Derby as a traditional client/server database server. To accomplish this, Derby is embedded in a server framework. A server framework is simply a piece of software that can accept and process network communication. Derby includes a Network Server, but you can also run Derby in a server framework of your choice.</p><p><a name="N101B2"><b>Figure 2. Derby acts as a client/server DBMS within a server framework</b></a><br /><img height="400" alt="" src="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/fig2.gif" width="529" /></p><p><a name="integration"><span class="atitle3">Easy Integration</span></a><br />It is just as easy to embed Derby inside a Java Web server, such as IBM WebSphere® Application Server. Figure 3 shows Derby embedded in a Java Web server, with clients accessing the database through Hypertext Transfer Protocol (HTTP ) requests to servlets. </p><p><a name="N101CE"><b>Figure 3. Derby embedded in Web server</b></a><br /><img height="320" alt="" src="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/fig3.gif" width="292" /></p><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#flexibility">Flexibility</a><p><a name="persistence"><span class="atitle3">Data Persistence</span></a><br />Derby provides persistence of data by storing data in disk files. A Derby engine can manage one or more database files, but each database file can only be accessed by a single Derby engine. In a client/server configuration, the engine provides multi-user access to the databases under its control. All threads that access the database do so through the database engine.</p><p><a name="N101EC"><b>Figure 4. Thread access to Derby</b></a><br /><img height="282" alt="" src="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/fig4.gif" width="559" /></p><p>The data in the database disk files is stored in a portable format so that databases can be easily transported from machine to machine regardless of the CPU architecture of the machines. Derby can also handle data files on read-only media. These characteristics make it easy to download Derby applications together with their database or run them from a CD-ROM. The combination of portable database formats and the pure Java DBMS engine makes it possible to send a data-centric application anywhere, either on media or over a network. </p><p><a name="flexibility"><span class="atitle3">Flexibility</span></a><br />Derby provides a great deal of flexibility for system designers. Each Derby instance can manage multiple databases, the databases can live on various media, and there's nothing to stop the application from connecting to other DBMS systems.</p><p><a name="N10208"><b>Figure 5. Flexible application architecture</b></a><br /><img height="260" alt="" src="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/fig5.gif" width="524" /></p><p><a name="cs_api"><span class="atitle3">Programmer's API</span></a><br />Applications access the database with the industry standard JDBC API. When a client application needs to store or retrieve data from the database, it submits a request through the JDBC API to the Derby engine (either over a network or directly to the embedded engine). The client program is not required to use any Derby-specific APIs; clients only use JDBC. The Derby driver is selected by supplying a Derby JDBC connection Uniform Resource Locator (URL), as shown in Listing 1. </p><a name="N10220"><b>Listing 1. Derby JDBC connection URL</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
Connection conn = DriverManager.getConnection("jdbc:derby:greetdb");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT message FROM greetings");
while (rs.next())
{
    String greeting = rs.getString(1);
    System.out.println(greeting);
}
rs.close();
stmt.close();
conn.close();</code></pre></td></tr></tbody></table><p>Since the application is only using JDBC APIs, it is portable, and will run against JDBC-compliant databases from other vendors. This standards compliance enables a developer to write an application against Derby and deploy it against another DBMS. </p><p>When a client requests data from the DBMS, the JDBC driver copies the data from the database into the application. Modifications of that data in the application, intentional or unintentional, do not affect the data in the database until JDBC calls are used to send the changes back to the DBMS. Similarly, when a client stores data in the DBMS, it is copied out of the client and into the DBMS. Because Derby is transactional, complex data manipulations can be grouped together into transactions. Derby guarantees data consistency by ensuring that even after system failure, either all or none of the transaction will commit to the database file. </p><p><a name="N10232"><b>Figure 6. Objects copied from DBMS to application</b></a><br /><img height="233" alt="" src="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/fig6.gif" width="354" /></p><p>More information about JDBC can be found at http://java.sun.com/jdbc. </p><p><a name="cs_jdbc"><span class="atitle3">JDBC drivers</span></a><br />To support the <a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_embed">embedded architecture</a>, Derby includes an embedded driver for accessing Derby from Java client programs. The embedded driver is used to communicate with Derby when it is running in the same JVM as the application program. </p><p>To support the <a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_cliserv">client-server architecture</a>, IBM Cloudscape includes the IBM DB2 UDB JDBC Universal Driver, which works with the Derby <a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_cliserv">Network Server</a>. </p><p>Both JDBC drivers are Type 4 drivers: pure Java on the client and connect directly to the database engine without any intermediate translation. </p><p>JDBC connection URLs to Derby databases all start with the prefix <code>jdbc:derby:</code>. The connection URL for an embedded database on drive e: might look like this: <code>jdbc:derby:e:/db</code>. The connection URL to a Network Server might look like this: <code>jdbc:derby:net://localhost:1527/mydb</code>.</p><p>Derby JDBC drivers support JDBC 2.0 and 3.0; they work with JVM 1.3.1 and beyond. The standard extensions for JNDI data sources, connection pooling, and XA are also provided. These features enable Derby to be a resource manager in a distributed J2EE system. </p><p><a name="cs_odbc"><span class="atitle3">CLI/ODBC driver</span></a><br />The IBM DB2 CLI/ODBC driver for Derby (beta) is available for free download from <a href="http://www-106.ibm.com/developerworks/cloudscape">IBM developerWorks</a>. In addition to supporting standard ODBC clients, it also makes it possible to develop PHP/Perl and .NET applications over ODBC. </p><p><a name="cs_rdbms"><span class="atitle2">RDBMS capabilities</span></a><br />Derby implements the SQL92E language standard and many features of SQL99, with extensions for Java. SQL language support includes the following: </p><ul><li>Basic types: CHAR, DECIMAL, DOUBLE PRECISION, FLOAT, INTEGER, NUMERIC, REAL, SMALLINT. 
</li><li>Datetime data types (from SQL92T): DATE, TIME, TIMESTAMP (with JDBC date/time escape syntax). 
</li><li>Other types: BIGINT, VARCHAR, CHAR FOR BIT DATA, VARCHAR FOR BIT DATA, LONG VARCHAR, LONG VARCHAR FOR BIT DATA, BLOB, CLOB. 
</li><li>Basic math operations: +,*,-,/,unary +,unary -. 
</li><li>Basic comparisons: &lt;,&gt;,&lt;=,&gt;=,&lt;&gt;,=. 
</li><li>Datetime literals 
</li><li>Built-in functions: ABS or ABSVAL, CAST, LENGTH, concatenation (||), NULLIF and CASE expressions, CURRENT_DATE, CURRENT_ISOLATION, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, DATE, DAY, HOUR, IDENTITY_VAL_LOCAL, LOCATE, LCASE or LOWER, LTRIM, MINUTE, MOD, MONTH, RTRIM, SECOND, SESSION_USER, SQRT, SUBSTR, TIME, TIMESTAMP, UCASE or UPPER, USER, YEAR. 
</li><li>Basic predicates: BETWEEN, LIKE, NULL 
</li><li>Quantified predicates: IN, ALL, ANY/SOME, EXISTS 
</li><li>CREATE and DROP SCHEMA 
</li><li>CREATE and DROP TABLE 
</li><li>Check constraints 
</li><li>ALTER TABLE: ADD COLUMN and ADD/DROP CONSTRAINT 
</li><li>CREATE and DROP VIEW 
</li><li>Constraints: NOT NULL, UNIQUE, PRIMARY KEY, CHECK, FOREIGN KEY 
</li><li>Cascade delete 
</li><li>Column defaults 
</li><li>Delimited identifiers 
</li><li>Updatable cursors (through JDBC) 
</li><li>Dynamic SQL (through JDBC) 
</li><li>INSERT, UPDATE, and DELETE statements 
</li><li>Positioned updates and deletes 
</li><li>WHERE qualifications 
</li><li>GROUP BY 
</li><li>HAVING 
</li><li>ORDER BY 
</li><li>UNION and UNION ALL 
</li><li>Subqueries as expressions (from SQL92F) 
</li><li>Joins in the WHERE clause 
</li><li>Joins (SQL92T): INNER, RIGHT OUTER, LEFT OUTER, named column join, condition join 
</li><li>Aggregate functions (with DISTINCT): AVG, COUNT, MAX, MIN, SUM 
</li><li>Select expressions 
</li><li>SELECT *, SELECT table.* (SQL92T), SELECT DISTINCT, select expressions 
</li><li>Named select columns 
</li><li>SQLSTATE 
</li><li>UNION in views (SQL92T) 
</li><li>CAST (SQL92T) 
</li><li>INSERT expressions (SQL92T): <code>insert into T2 (COL) select col from T1</code></li><li>VALUES expressions: <code>select * from (values (1, 2)) as foo(x, y)</code>, etc. 
</li><li>Triggers </li></ul><p>Other traditional database features include:</p><ul><li>Cost-based query optimizer: join order, index selection,bulk fetching, join strategies (nested loop or hash), sort avoidance, lock escalation, subquery flattening, transitive closure, and many other query transformations. It uses a unique sampling technique that requires no intervention for statistical gathering, and also provides query plan overrides and statistics on actual query results. 
</li><li>Multi-column B-Tree indexes 
</li><li>Unlimited length rows, and the capability of "streaming" column values to the client 
</li><li>Data import/export with fast-load and fast-create index 
</li><li>Transaction commit and rollback 
</li><li>Transactional isolation (serializable, repeatable read, read committed, or dirty read) 
</li><li>Crash recovery 
</li><li>Multiple concurrent connections 
</li><li>Multi-threaded connections 
</li><li>Row locking with escalation to table locks 
</li><li>User and connection authentication: built-in SH3, application-defined, or through external user authentication systems (LDAP, JNDI) 
</li><li>Diagnostics and consistency checks 
</li><li>Online database backup 
</li><li>Locales 
</li><li>Database encryption 
</li><li>Script tool, <code>ij</code></li><li>DDL examination tool, <code>dblook</code></li><li>Modify column to change its length or nullability 
</li><li>Identity default for automatic sequence numbers for data </li></ul><p><a name="cs_security"><span class="atitle2">Security features</span></a><br />Derby includes security features for protecting database access using standard Java features, encrypting databases so they may be deployed to remote sites and for integrating them into existing user authentication schemes.</p><p><a name="N1035A"><span class="atitle3">Java 2 Security Manager</span></a><br />Derby can be run under a Java 2 security manager. In addition to support policy files, Derby can detect digital signatures on JAR files. Before loading a class from a signed JAR file stored in the database, Derby will verify the JAR was signed with a X.509 certificate and verify the validity of the signature. </p><p><a name="cs_encrypt"><span class="atitle3">Encrypted databases</span></a><br />When a database is deployed to a remote or mobile location, it is not possible to use physical security to prevent unauthorized access to data. If the data files can be read, a sophisticated user could decode the information they contain. The only way to secure data in this environment is to encrypt it on disk. That way, simply being able to read the database files does not reveal the data.</p><p>Derby supports secure remote data database encryption. All data in such a database is decrypted by the database engine when read and re-encrypted when written back to disk. No data exists in clear-text form in the database files. Since the entire database is encrypted, the structure of the database schema is also hidden. </p><p>For an encrypted database to be usable, a boot password must be provided when the database is first started. This is a separate password from the usual database connection username and password (which must also be supplied to access the database). Without the boot password, the database will not start. </p><p>Database encryption is useful for applications that distribute databases to locations where physical security of the files cannot be guaranteed. For example, mobile databases on notebook computers can be stolen if the notebook computer is stolen. Applications that are installed on remote multi-user machines are subject to unauthorized access if the remote administrator does not appropriately protect the files. </p><p>Database encryption adds less than 10% performance overhead, and takes no additional disk space. It is based on the 1.2.1 version of the Java Cryptographic Extension (JCE). Data Encryption Standard (DES) is used as a default encryption method, or the user can configure which algorithm to use.</p><p><a name="cs_auth"><span class="atitle3">External user authentication</span></a><br />Derby also provides support for integrating into external user authentication schemes. Rather than maintaining an internal list of authorized users, Derby can be configured to check with an external authentication service. Lightweight Directory Access Protocol (LDAP) support is provided, and custom schemes are supported with user-defined Java Naming and Directory Interface (JNDI) classes. </p><p>Not having the user names and passwords maintained in the database means less administrative overhead (to transfer names into the database). This is especially important in deployed server applications, which must be deployed with as little administrative overhead as possible.</p><p>LDAP is an emerging Internet standard that provides an open directory access protocol running over TCP/IP. Windows NT ™ domain user authentication can be provided through LDAP by using the Netscape NT Synchronization Service.</p><p>Because Derby is an embedded system, it provides simple user authorization controls. Users can be restricted to read-only access or restricted from any access on a per-system or per-database level. This ensures only permitted, authenticated users access or modify a database.</p><p><a name="cs_extensions"><span class="atitle2">Java extensions</span></a><br />Derby supports the following Java extensions:</p><ul><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_func">Java functions</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_trigger">Using Java functions in triggers</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_sp">Java stored procedures</a></li><li><a href="http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0408anderson/index.html#cs_load">Loading Java classes from the database</a></li></ul><p>The examples in the following sections assume that the <code>ADDRESS</code> table exists in a database along with the <code>ZIPMAP</code> table that maps ZIP codes to cities. Listing 2 shows the schema for the tables. </p><a name="N103BE"><b>Listing 2. Sample schema</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
CREATE TABLE address (name VARCHAR(60), street VARCHAR(255), zipcode INTEGER)
CREATE TABLE zipmap  (zipcode INTEGER, city VARCHAR(255))</code></pre></td></tr></tbody></table><p><a name="cs_func"><span class="atitle3">Java functions</span></a><br />A Java function consists of Java code that is callable from SQL. A Java function can be invoked anywhere in a SQL statement that a SQL expression is allowed. It can also be invoked with the <code>VALUES</code> key word and it can be invoked inside a trigger. </p><p>For example, Listing 3 creates a function in the ZipMap class that looks up the name of a city given its ZIP code (error and exception-handling code are ommitted for brevity). </p><a name="N103D8"><b>Listing 3. Sample function source code</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
public class ZipMap
{    
    public static String cityFromZipFunc (int zipcode)
    {
        String city;
        Connection conn = DriverManager.getConnection("jdbc:default:connection");
        String query = "SELECT city FROM zipmap WHERE zipcode = ?";
        PreparedStatement ps = conn.prepareStatement(query);
        ps.setInt(1, zipcode);
        ResultSet rs = ps.executeQuery();
        if (!rs.next())
            city = "Unknown City";
        else
            city = rs.getString(1);
        rs.close();
        ps.close();
        conn.close();
        return city;
    }
}</code></pre></td></tr></tbody></table><p>The SQL statement that creates a function for this Java code is shown in Listing 4: </p><a name="N103E5"><b>Listing 4. Sample <code>CREATE FUNCTION</code></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
CREATE FUNCTION cityFromZip (zipcode INTEGER)
RETURNS VARCHAR(255)
LANGUAGE JAVA 
PARAMETER STYLE JAVA
READS SQL DATA
EXTERNAL NAME 'com.acme.sales.ZipMap.cityFromZipFunc'
</code></pre></td></tr></tbody></table><p>Once the <code>cityFromZip</code> function has been created, it can be invoked using the<code>VALUES</code> keyword or by referencing it in the SQL statement as shown in Listing 5.</p><a name="N103FD"><b>Listing 5. Sample cityFromZip function usage</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

VALUES cityFromZip(94105)

SELECT name, street, cityFromZip(zipcode), zipcode 
FROM   address

SELECT * 
FROM   address 
WHERE  cityFromZip(zipcode)='San Francisco'
</code></pre></td></tr></tbody></table><p><a name="cs_trigger"><span class="atitle3">Using Java functions in triggers</span></a><br />A SQL trigger specifies the actions that should occur when a SQL statement inserts into, updates, or deletes from a table. A Java function can be used in a trigger, for example, to provide e-mail notification about updates, perform updates in other databases through JDBC, or anything that can be done using Java programming. Here's an example of a trigger that calls a Java function to send an e-mail alert when a new record is inserted into the <code>ADDRESS</code> table. </p><a name="N10414"><b>Listing 6. Sample function in a trigger</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
CREATE TRIGGER newaddress AFTER INSERT ON address
REFERENCING NEW as NEW
FOR EACH ROW MODE DB2SQL
    VALUES(sendEmailAlert(NEW.name)) </code></pre></td></tr></tbody></table><p><a name="cs_sp"><span class="atitle3">Java stored procedures</span></a><br />A Java stored procedure consists of Java code that is callable from SQL, runs in the database server, and accesses the database. </p><p>Derby supports the <code>CALL</code> statement for invoking a Java stored procedure. Derby supplies <code>JDBC OUT</code> parameters so that values can be returned in parameters; this is done by having the Java parameter be an array of the type desired. For example, an <code>int</code> parameter would be declared as <code>int[]</code> and a <code>String</code> parameter would be declared as <code>String[]</code> so a value could be returned in it. If the stored procedure includes any <code>OUT</code> parameters, it needs to be invoked from a client application using the <code>CallableStatement</code> interface. If the stored procedure does not include any <code>OUT</code> parameters, it can also be invoked using <code>CALL</code> in a tool that executes dynamic SQL statements, such as the <code>ij</code> tool.</p><p>A Java stored procedure that looks up the name of a city given a ZIP code might look like this (again, error-and exception- handling code are omitted for brevity):</p><a name="N10459"><b>Listing 7. Sample stored procedure source code</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
public class ZipMap
{    
    public static void cityFromZipSp (int zipcode, String[] outCity)
    {
        Connection conn = DriverManager.getConnection("jdbc:default:connection");
        String query = "SELECT city FROM zipmap WHERE zipcode = ?";
        PreparedStatement ps = conn.prepareStatement(query);
        ps.setInt(1, zipcode);
        ResultSet rs = ps.executeQuery();
        if (!rs.next())
            outCity[0] = "Unknown City";
        else
            outCity[0] = rs.getString(1);
        rs.close();
        ps.close();
        conn.close();
        return;
    }
}</code></pre></td></tr></tbody></table><p>The stored procedure would be created as shown in Listing 8:</p><a name="N10466"><b>Listing 8. Sample <code>CREATE PROCEDURE</code></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
CREATE PROCEDURE cityFromZip ( IN zipcode INTEGER, OUT city VARCHAR(255) )
PARAMETER STYLE JAVA
LANGUAGE JAVA 
READS SQL DATA
EXTERNAL NAME 'com.acme.sales.ZipMap.cityFromZipSp'
</code></pre></td></tr></tbody></table><p>As mentioned above, if a stored procedure includes <code>OUT</code> parameters, a client application executes the stored procedure using the <code>CallableStatement</code> method, as shown in Listing 9.</p><a name="N1047E"><b>Listing 9. Sample client calling stored procedure</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
  public static String callOutCity(int zipCode, Connection con)
  {
    String outCity = "";
    try
    {
      // prepare the CALL statement 
      String procName = "CITYFROMZIP";
      String sql = "CALL " + procName + "(?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      // Set the input parameter to the zipcode
      callStmt.setInt(1, zipCode);

      // Register the output parameter
      callStmt.registerOutParameter(2, Types.CHAR);

      // call the stored procedure
      System.out.println();
      System.out.println("Call stored procedure named " + procName);
      callStmt.execute();

      // retrieve output parameters
      outCity = callStmt.getString(2);
      System.out.println("City name is "
                         + outCity);
    }
    catch (SQLException e)
    {
      System.out.println(e);
    }
    return(outCity);
  }
</code></pre></td></tr></tbody></table><p><a name="cs_load"><span class="atitle3">Loading Java classes from the database</span></a><br />Java stored procedures, functions, or other application logic can be stored in the database along with the data, and the database can be programmed to load the classes from there. This makes the database data and its logic into a single, self-contained application package. This simplifies application deployment, because it reduces the potential for problems with a user's class path. It is particularly useful when the classes stored in the database contain the logic for data types used in columns in the database -- the database will not be dependent on anything outside of it.</p><p>Storage and loading of application classes in the database is provided by two mechanisms: the ability to add, remove and replace JAR files in the database,and the ability to add these JAR files to the class path.</p><p>Once the application code has been added to the database, moving or copying the database ensures that the appropriate application logic is moved along with the data. This means that object data will never be separated from its methods.</p><p><a name="value_add"><span class="atitle2">IBM Cloudscape Features</span></a><br />The Derby software includes so many capabilities, what more could you possibly want? </p><p>The IBM Cloudscape commercial product ships Derby and additionally includes: </p><ul><li>Technical Support 
</li><li>IBM DB2 UDB Universal JDBC Driver for use with the Derby Network Server 
</li><li>The IBM Cloudscape manuals, including translated manuals 
</li><li><a href="http://publib.boulder.ibm.com/infocenter/cldscp10/index.jsp">The Cloudscape Information Center</a></li><li>Translated error messages 
</li><li>Features to jump start developers who are new to Java and/or Derby, including: 
<ul><li>Product installers for Windows and Linux that install the Java Runtime Environment (JRE) and generate Windows batch files and UNIX scripts for setting the environment. 
</li><li>Sample databases. </li></ul></li></ul><p><a name="cs_summary"><span class="atitle2">Summary</span></a><br />In this article you learned about the fundamental architecture of Derby and how it can be embedded inside a client or a server application. You learned about the use of standard JDBC calls to manipulate the data. You received a summary of the security mechanisms available in Derby. You also learned how to take advantage of Java integration to create Java functions and stored procedures.</p><p>Derby provides developers with a small footprint, standards-based Java database that can be tightly embedded into any Java-based solution. With its combination of robust SQL features, support for Java technology, security, and embeddable, pure Java architecture, Derby is the data management product of choice for data-driven Java applications. The value that the IBM Cloudscape commercial offering adds makes it easier than ever for Java developers to deploy their Java-based solutions.</p><img src ="http://www.blogjava.net/mstar/aggbug/5812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-09 13:23 <a href="http://www.blogjava.net/mstar/archive/2005/06/09/5812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在试XML-RPC（这回画个图吧）</title><link>http://www.blogjava.net/mstar/archive/2005/06/07/5705.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 07 Jun 2005 12:15:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/07/5705.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5705.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/07/5705.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5705.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5705.html</trackback:ping><description><![CDATA[<P>上次那个“小试”实在太不过瘾，这次我们从服务器上定时取出数据，然后用javascript在浏览器上画个图。<IMG height=19 src="http://www.blogjava.net/Emoticons/teeth_smile.gif" width=19 border=0><BR>这回我就不具体解释了，就贴一下程序啦！！<BR><BR>先来个图吧：<BR><IMG height=768 alt=xml-rpc3.jpg src="http://www.blogjava.net/images/blogjava_net/mstar/pics/xml-rpc3.jpg" width=1024 border=0><BR><BR>&nbsp;用javascript画图，当然不能空手画了，找个库吧。wz_jsgraphics，详细资料看这里（<A href="http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm#download">http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm#download</A>）很酷的一个javascript图形库，当然除了画图还有别的，不妨试试（我一年前发现的这东东，现实才第一次用<IMG height=20 src="http://www.blogjava.net/Emoticons/hitwall.gif" width=25 border=0>）用起来跟用c画图差不多。<BR>&nbsp;首先当然还是建个服务的类：<BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG id=Codehighlighter1_25_269_Open_Image onclick="this.style.display='none'; Codehighlighter1_25_269_Open_Text.style.display='none'; Codehighlighter1_25_269_Closed_Image.style.display='inline'; Codehighlighter1_25_269_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_25_269_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_25_269_Closed_Text.style.display='none'; Codehighlighter1_25_269_Open_Image.style.display='inline'; Codehighlighter1_25_269_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align=top><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;DataService&nbsp;</SPAN><SPAN id=Codehighlighter1_25_269_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_25_269_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top><BR><IMG id=Codehighlighter1_52_117_Open_Image onclick="this.style.display='none'; Codehighlighter1_52_117_Open_Text.style.display='none'; Codehighlighter1_52_117_Closed_Image.style.display='inline'; Codehighlighter1_52_117_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_52_117_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_52_117_Closed_Text.style.display='none'; Codehighlighter1_52_117_Open_Image.style.display='inline'; Codehighlighter1_52_117_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;getData()</SPAN><SPAN id=Codehighlighter1_52_117_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_52_117_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;r&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Random();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">20</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">10</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">r.nextDouble()</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">5</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;<BR><IMG id=Codehighlighter1_160_267_Open_Image onclick="this.style.display='none'; Codehighlighter1_160_267_Open_Text.style.display='none'; Codehighlighter1_160_267_Closed_Image.style.display='inline'; Codehighlighter1_160_267_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_160_267_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_160_267_Closed_Text.style.display='none'; Codehighlighter1_160_267_Open_Image.style.display='inline'; Codehighlighter1_160_267_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">static</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;main(String[]&nbsp;args)</SPAN><SPAN id=Codehighlighter1_160_267_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_160_267_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DataService&nbsp;ds&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;DataService();<BR><IMG id=Codehighlighter1_223_264_Open_Image onclick="this.style.display='none'; Codehighlighter1_223_264_Open_Text.style.display='none'; Codehighlighter1_223_264_Closed_Image.style.display='inline'; Codehighlighter1_223_264_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_223_264_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_223_264_Closed_Text.style.display='none'; Codehighlighter1_223_264_Open_Image.style.display='inline'; Codehighlighter1_223_264_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">for</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">&nbsp;i</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;i</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">20</SPAN><SPAN style="COLOR: #000000">;i</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">)</SPAN><SPAN id=Codehighlighter1_223_264_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_223_264_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.</SPAN><SPAN style="COLOR: #0000ff">out</SPAN><SPAN style="COLOR: #000000">.println(ds.getData());<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN></DIV></DIV><BR>Servlet上篇文章里有，我这里就不写了。<BR>然后是一个html页面。<BR>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">html</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">head</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">title</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">XML-RPC</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">title</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/init.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/lib/urllib.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/lib/xml.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/lib/xmlrpc.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/hello.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/wz_jsgraphics.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">head</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">body&nbsp;</SPAN><SPAN style="COLOR: #ff0000">onload</SPAN><SPAN style="COLOR: #0000ff">="init()"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="button"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="do3"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="start"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;onclick</SPAN><SPAN style="COLOR: #0000ff">="startTimer()"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="button"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="do3"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="stop"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;onclick</SPAN><SPAN style="COLOR: #0000ff">="stopTimer()"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="result"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">div&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="canvas"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;style</SPAN><SPAN style="COLOR: #0000ff">="border:1px&nbsp;solid&nbsp;#6666FF;&nbsp;position:relative;height:300px;width:500;&nbsp;background-color:#CCCCCC"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">div</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">textarea&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="debug"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;rows</SPAN><SPAN style="COLOR: #0000ff">="10"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;name</SPAN><SPAN style="COLOR: #0000ff">="S1"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;cols</SPAN><SPAN style="COLOR: #0000ff">="69"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">textarea</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">body</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">html</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV></DIV>重点是这个div，我们用wz_jsgraphics画图就是往这上面画，他的作用相当于很多画图系统的canvas.其实主要是提供参考坐标。<BR>然后就是重点了hello.js(懒得该名字了)<BR>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;timerid;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;dataservice;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;g;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;xPoints&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Array();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;y1Points&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Array();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;y2Points&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Array();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;length</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>startTimer&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;init();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(timerid</SPAN><SPAN style="COLOR: #000000">==</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timerid&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;window.setInterval(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">doTask()</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">800</SPAN><SPAN style="COLOR: #000000">);&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>stopTimer&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;window.clearInterval(timerid);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;timerid</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">&nbsp;init(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;importModule(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">xmlrpc</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">(e){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reportException(e);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throw</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">importing&nbsp;of&nbsp;xmlrpc&nbsp;module&nbsp;failed.</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;addr&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">http://localhost:8080/Rpc/RpcServer</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;methods&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;[</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">DataService.getData</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">];<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataservice&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc.ServiceProxy(addr,&nbsp;methods);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">(e){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;em;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(e.toTraceString){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;em&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;e.toTraceString();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;em&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;e.message;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alert(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Error&nbsp;trace:&nbsp;\n\n</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">&nbsp;em);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;jsGraphics(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">canvas</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">&nbsp;doTask(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;rslt&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;dataservice.DataService.getData();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;document.getElementById(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">result</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">).value</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">rslt;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;y1&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">145</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">rslt</SPAN><SPAN style="COLOR: #000000">/</SPAN><SPAN style="COLOR: #000000">20</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;rslt&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;dataservice.DataService.getData();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;y2&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">145</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">rslt</SPAN><SPAN style="COLOR: #000000">/</SPAN><SPAN style="COLOR: #000000">20</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(length</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">44</SPAN><SPAN style="COLOR: #000000">){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y1Points[length]</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">y1;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y2Points[length]</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">y2;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">for</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;i</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;i</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">y1Points.length;i</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xPoints[i]&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">10</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">i;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">for</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;i</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;i</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">y1Points.length</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;i</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y1Points[i]&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;y1Points[i</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">];<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y2Points[i]&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;y2Points[i</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">];<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y1Points[length]</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">y1;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y2Points[length]</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">y2;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.clear();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;drawbg();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;drawline();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;debug();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">&nbsp;drawline(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.setStroke(</SPAN><SPAN style="COLOR: #000000">2</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">for</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;i</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">;i</SPAN><SPAN style="COLOR: #000000">&lt;=</SPAN><SPAN style="COLOR: #000000">y1Points.length;i</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.setColor(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">yellow</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(xPoints[i</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">],y1Points[i</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">],xPoints[i],y1Points[i])<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.setColor(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">red</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(xPoints[i</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">],y2Points[i</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">],xPoints[i],y2Points[i])<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">g.drawPolyline(xPoints,yPoints);</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;g.paint();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">&nbsp;drawbg(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.setStroke(</SPAN><SPAN style="COLOR: #000000">2</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.setColor(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">maroon</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">270</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">470</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">270</SPAN><SPAN style="COLOR: #000000">);</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">x轴</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">270</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">10</SPAN><SPAN style="COLOR: #000000">);</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">y轴</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">10</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">26</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">14</SPAN><SPAN style="COLOR: #000000">);</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">x轴箭头</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">10</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">34</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">14</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(</SPAN><SPAN style="COLOR: #000000">470</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">270</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">466</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">266</SPAN><SPAN style="COLOR: #000000">);</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">y轴箭头</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(</SPAN><SPAN style="COLOR: #000000">470</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">270</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">466</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">274</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.setColor(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">#000000</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">画刻度</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;x&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">for</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;y&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">270</SPAN><SPAN style="COLOR: #000000">;y&nbsp;</SPAN><SPAN style="COLOR: #000000">&gt;=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">20</SPAN><SPAN style="COLOR: #000000">;y&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;y&nbsp;</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">50</SPAN><SPAN style="COLOR: #000000">&nbsp;){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(x</SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">,y,x</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">,y);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.setStroke(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.drawLine(</SPAN><SPAN style="COLOR: #000000">30</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">145</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">470</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">145</SPAN><SPAN style="COLOR: #000000">);</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">中心线</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.drawString(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">23</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">271</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.drawString(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Time</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">430</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">271</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;g.paint();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">&nbsp;debug(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;debug&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">for</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;i</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">;i</SPAN><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">xPoints.length;i</SPAN><SPAN style="COLOR: #000000">++</SPAN><SPAN style="COLOR: #000000">){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;debug&nbsp;</SPAN><SPAN style="COLOR: #000000">+=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">x:</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">i</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">xPoints[i]</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;y1:</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">i</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">y1Points[i]</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;y2:</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">i</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">y2Points[i]</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">\n</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;document.getElementById(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">debug</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">).value</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">debug;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV></DIV>其实没什么复杂的，不过就是javascript调试起来太费事了。错又不知道错在哪里。还是喜欢java，调试起来容易多了！<BR>其中一些算法我做的也不太好，有很多需要改进的地方。主要就是如何把从其服务器得到的元数据转换成画图用的数据（两个数组，存放二维坐标集）。<BR>我画了两条线，一条线太单薄了，呵呵。两条线用的x轴坐标相同（其实是时间），所以就用一个数组了。<img src ="http://www.blogjava.net/mstar/aggbug/5705.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-07 20:15 <a href="http://www.blogjava.net/mstar/archive/2005/06/07/5705.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>小试XML-RPC（浏览器javascript与服务器java通信）</title><link>http://www.blogjava.net/mstar/archive/2005/06/07/5657.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 07 Jun 2005 05:50:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/07/5657.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5657.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/07/5657.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5657.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5657.html</trackback:ping><description><![CDATA[<P>前些天无意中发现了XML-RPC（不过笑我才发现啊<IMG height=20 src="http://www.blogjava.net/Emoticons/hitwall.gif" width=25 border=0>），总想找个机会摆弄摆弄。毕业论文基本上弄完了，所以决定今天把它弄明白。<BR>XML-RPC的最大用处，我首先想到的是浏览器在不刷新页面的情况下与服务器通信，请求数据。下面我就说一下我用XML-RPC是怎么实现的。</P>
<P><STRONG>第一步：选择XML-RPC实现。<BR></STRONG>XML-RPC的一个很大优势就是 它是一个标准，并且各种开发环境下都有实现（酷），这是它能够轻松跨平台的原因。<BR>javascript有3个实现。我看了一下最好的应该是jsolait（JavaScript o Lait）的实现了。因为他不仅仅是一个xml-rpc的实现，除此之外还有很多javascript库,详细内容请看这里（<A href="http://jsolait.net/">http://jsolait.net/</A>）。<BR>java的实现就更多了，我当然毫不犹豫地选择apache的。详细内容看这里（<A href="http://ws.apache.org/xmlrpc/">http://ws.apache.org/xmlrpc/</A>）</P>
<P>第二步：建立服务。<BR>用java建立xml-rpc有两种方式，一种是单独开个端口，一种是用servlet。我们客户端是用javascript，那么服务端用servlet是再好不过的了。<BR>如何使用apache的xml-rpc，请详细看apache的资料。（大哥你不会连servlet也不会建吧，那你还是不要往下看了）。<BR>代码如下：<BR>这是一个sayHello的服务类：<BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG id=Codehighlighter1_26_102_Open_Image onclick="this.style.display='none'; Codehighlighter1_26_102_Open_Text.style.display='none'; Codehighlighter1_26_102_Closed_Image.style.display='inline'; Codehighlighter1_26_102_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_26_102_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_26_102_Closed_Text.style.display='none'; Codehighlighter1_26_102_Open_Image.style.display='inline'; Codehighlighter1_26_102_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align=top><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;HelloService&nbsp;</SPAN><SPAN id=Codehighlighter1_26_102_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_26_102_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;<BR><IMG id=Codehighlighter1_66_99_Open_Image onclick="this.style.display='none'; Codehighlighter1_66_99_Open_Text.style.display='none'; Codehighlighter1_66_99_Closed_Image.style.display='inline'; Codehighlighter1_66_99_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_66_99_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_66_99_Closed_Text.style.display='none'; Codehighlighter1_66_99_Open_Image.style.display='inline'; Codehighlighter1_66_99_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;String&nbsp;sayHello(String&nbsp;name)</SPAN><SPAN id=Codehighlighter1_66_99_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_66_99_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Hello:&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">name</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;!</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV></DIV>
<P><BR>下面是一个Math服务类：<BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG id=Codehighlighter1_25_328_Open_Image onclick="this.style.display='none'; Codehighlighter1_25_328_Open_Text.style.display='none'; Codehighlighter1_25_328_Closed_Image.style.display='inline'; Codehighlighter1_25_328_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_25_328_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_25_328_Closed_Text.style.display='none'; Codehighlighter1_25_328_Open_Image.style.display='inline'; Codehighlighter1_25_328_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align=top><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;MathService&nbsp;</SPAN><SPAN id=Codehighlighter1_25_328_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_25_328_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG id=Codehighlighter1_55_174_Open_Image onclick="this.style.display='none'; Codehighlighter1_55_174_Open_Text.style.display='none'; Codehighlighter1_55_174_Closed_Image.style.display='inline'; Codehighlighter1_55_174_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_55_174_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_55_174_Closed_Text.style.display='none'; Codehighlighter1_55_174_Open_Image.style.display='inline'; Codehighlighter1_55_174_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;add(Vector&nbsp;v)</SPAN><SPAN id=Codehighlighter1_55_174_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_55_174_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;a&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Double.parseDouble((String)v.</SPAN><SPAN style="COLOR: #0000ff">get</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;b&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Double.parseDouble((String)v.</SPAN><SPAN style="COLOR: #0000ff">get</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;a</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">b;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;<BR><IMG id=Codehighlighter1_207_326_Open_Image onclick="this.style.display='none'; Codehighlighter1_207_326_Open_Text.style.display='none'; Codehighlighter1_207_326_Closed_Image.style.display='inline'; Codehighlighter1_207_326_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_207_326_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_207_326_Closed_Text.style.display='none'; Codehighlighter1_207_326_Open_Image.style.display='inline'; Codehighlighter1_207_326_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;mult(Vector&nbsp;v)</SPAN><SPAN id=Codehighlighter1_207_326_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_207_326_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;a&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Double.parseDouble((String)v.</SPAN><SPAN style="COLOR: #0000ff">get</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">double</SPAN><SPAN style="COLOR: #000000">&nbsp;b&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Double.parseDouble((String)v.</SPAN><SPAN style="COLOR: #0000ff">get</SPAN><SPAN style="COLOR: #000000">(</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">));<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;a</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">b;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN></DIV></DIV>
<P><BR>接着是Servlet啦，作为RPC Server用的，这段代码比较经典，很多资料上都有。<BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG id=Codehighlighter1_43_554_Open_Image onclick="this.style.display='none'; Codehighlighter1_43_554_Open_Text.style.display='none'; Codehighlighter1_43_554_Closed_Image.style.display='inline'; Codehighlighter1_43_554_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_43_554_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_43_554_Closed_Text.style.display='none'; Codehighlighter1_43_554_Open_Image.style.display='inline'; Codehighlighter1_43_554_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align=top><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;RpcServer&nbsp;extends&nbsp;HttpServlet&nbsp;</SPAN><SPAN id=Codehighlighter1_43_554_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_43_554_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;doPost(HttpServletRequest&nbsp;request,<BR><IMG id=Codehighlighter1_166_552_Open_Image onclick="this.style.display='none'; Codehighlighter1_166_552_Open_Text.style.display='none'; Codehighlighter1_166_552_Closed_Image.style.display='inline'; Codehighlighter1_166_552_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_166_552_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_166_552_Closed_Text.style.display='none'; Codehighlighter1_166_552_Open_Image.style.display='inline'; Codehighlighter1_166_552_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;HttpServletResponse&nbsp;response)&nbsp;throws&nbsp;ServletException,&nbsp;IOException&nbsp;</SPAN><SPAN id=Codehighlighter1_166_552_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"><IMG src="http://www.blogjava.net/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_166_552_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;XmlRpcServer&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlRpcServer();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;xmlrpc.addHandler(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">HelloService</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;HelloService());<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;xmlrpc.addHandler(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">MathService</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;MathService());<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[]&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc.execute(request.getInputStream());<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;response.setContentType(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">text/xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;response.setContentLength(result.length);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;OutputStream&nbsp;</SPAN><SPAN style="COLOR: #0000ff">out</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;response.getOutputStream();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">out</SPAN><SPAN style="COLOR: #000000">.write(result);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">out</SPAN><SPAN style="COLOR: #000000">.flush();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN></DIV></DIV>
<P>&nbsp;</P>
<P>主要是这三句：<BR>XmlRpcServer xmlrpc = new XmlRpcServer();<BR>xmlrpc.addHandler("HelloService", new HelloService());<BR>xmlrpc.addHandler("MathService",new MathService());<BR>一定要记牢Handler的名字，就是第一个参数，因为客户端就靠他来表示要调用的方法呢。</P>
<P>行了现在可以在web.xml中写入配置了：<BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">RpcServer</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">org.mstar.rpc.RpcServer</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">RpcServer</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">/RpcServer</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV></DIV>
<P><BR>至此，服务端的工作已经完成，启动应用服务器就行了。</P>
<P>下面是javacript的实现，这也是难点（其实不难理解，只是没有中文材料）。<BR>把jsolait的库下来以后解压缩，得到一些js文件，具体我就不说了。</P>
<P>建立一个html文件：<BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">html</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">head</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">title</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">XML-RPC</SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">title</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/init.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/lib/urllib.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/lib/xml.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/lib/xmlrpc.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">script&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text/javascript"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;src</SPAN><SPAN style="COLOR: #0000ff">="./js/hello.js"</SPAN><SPAN style="COLOR: #0000ff">&gt;&lt;/</SPAN><SPAN style="COLOR: #800000">script</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">head</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>a:</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="a"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">/&gt;&lt;</SPAN><SPAN style="COLOR: #800000">br</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>b:</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="b"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">/&gt;&lt;</SPAN><SPAN style="COLOR: #800000">br</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="button"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="do1"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="a+b"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;onclick</SPAN><SPAN style="COLOR: #0000ff">="add()"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="button"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="do2"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;value</SPAN><SPAN style="COLOR: #0000ff">="say"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;onclick</SPAN><SPAN style="COLOR: #0000ff">="hello()"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">input&nbsp;</SPAN><SPAN style="COLOR: #ff0000">type</SPAN><SPAN style="COLOR: #0000ff">="text"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;id</SPAN><SPAN style="COLOR: #0000ff">="result"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">&lt;/</SPAN><SPAN style="COLOR: #800000">html</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV></DIV>
<P><BR>注意到前面那一堆javascript的引用吗？就这么写吧。可别把hello.js当成solait的东西啦（看名字也知道啦），你是找不到的。这是我们自己写的：<BR>hello.js<BR></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">hello&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;importModule(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">xmlrpc</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">(e){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reportException(e);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throw</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">importing&nbsp;of&nbsp;xmlrpc&nbsp;module&nbsp;failed.</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;addr&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">http://localhost:8080/Rpc/RpcServer</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;methods&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;[</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">HelloService.sayHello</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">];<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;rslt;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;service&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc.ServiceProxy(addr,&nbsp;methods);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rslt&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;service.HelloService.sayHello(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">MTY</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">(e){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;em;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(e.toTraceString){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;em&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;e.toTraceString();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;em&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;e.message;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rslt&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Error&nbsp;trace:&nbsp;\n\n</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">&nbsp;em;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;document.getElementById(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">result</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">).value</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">rslt;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>add&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">function</SPAN><SPAN style="COLOR: #000000">(){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;a&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;document.getElementById(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">a</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">).value;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;b&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;document.getElementById(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">b</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">).value;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;params&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Array();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;params[</SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">]&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;a;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;params[</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">]&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;b;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;importModule(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">xmlrpc</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">(e){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reportException(e);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throw</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">importing&nbsp;of&nbsp;xmlrpc&nbsp;module&nbsp;failed.</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;addr&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">http://localhost:8080/Rpc/RpcServer</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;methods&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;[</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">HelloService.sayHello</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">MathService.add</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">];<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;rslt;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;service&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc.ServiceProxy(addr,&nbsp;methods);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rslt&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;service.MathService.add(params);<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">(e){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">var</SPAN><SPAN style="COLOR: #000000">&nbsp;em;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(e.toTraceString){<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;em&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;e.toTraceString();<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;em&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;e.message;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rslt&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Error&nbsp;trace:&nbsp;\n\n</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">&nbsp;em;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>&nbsp;document.getElementById(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">result</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">).value</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">rslt;<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top>}<BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align=top></SPAN></DIV></DIV>
<P><BR>这个js文件中有两个函数，一个负责从sayhello，一个负责加法运算。<BR>这里需要一些解释的地方：<BR>1、<BR>&nbsp;var xmlrpc=null;<BR>&nbsp;try{<BR>&nbsp;&nbsp;&nbsp; &nbsp;var xmlrpc = importModule("xmlrpc");<BR>&nbsp;}catch(e){<BR>&nbsp;&nbsp;&nbsp; &nbsp;reportException(e);<BR>&nbsp;&nbsp;&nbsp; &nbsp;throw "importing of xmlrpc module failed.";<BR>&nbsp;}<BR>这里是把xmlrpc模块引进来,你也就这么写吧，我也不知道为什么。<BR>2、<BR>&nbsp;var addr = "<A href="http://localhost:8080/Rpc/RpcServer">http://localhost:8080/Rpc/RpcServer</A>";<BR>&nbsp;var methods = ["HelloService.sayHello"];<BR>定义服务地址和要用的方法名。规则大概你也能看懂：Handler名.方法名。这里的Handler名就是你在xmlrpcServer中注册名，就是我上面让你记住的那个。方法名就是那个类自己的方法名。注意，methods是一个数组，所以可以写多个方法，如第二个例子。var methods = ["HelloService.sayHello","MathService.add"];<BR>3、<BR>&nbsp;&nbsp;&nbsp; try{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var service = new xmlrpc.ServiceProxy(addr, methods);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rslt = service.HelloService.sayHello("MTY");<BR>&nbsp;&nbsp;&nbsp; }catch(e){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var em;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(e.toTraceString){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; em = e.toTraceString();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; em = e.message;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rslt = "Error trace: \n\n" + em;<BR>&nbsp;&nbsp;&nbsp; }<BR>通过new xmlrpc.ServiceProxy(addr, methods);得到服务代理。<BR>然后调用服务的方法就行了，方法就是代理.Handler名.方法名（参数）。好像参数只能有一个，在第二个例子中我开始有两个参数a,b会发生错误。怎么办？没办法，在javascript用Array传参数，在java用Vector接参数（为什么用Vector，因为xml-rpc规范中的Array，apache使用Vector实现的，为什么javascript不用Vector，因为js没有Vector，且js的的Array是可变长的）。当然这就需要很多java端类型转换工作，js是弱类型的就不用转换了。</P>
<P><IMG height=768 alt=xmlrpc.jpg src="http://www.blogjava.net/images/blogjava_net/mstar/pics/xmlrpc.jpg" width=1024 border=0><BR><IMG height=768 alt=xmlrpc2.jpg src="http://www.blogjava.net/images/blogjava_net/mstar/pics/xmlrpc2.jpg" width=1024 border=0></P><img src ="http://www.blogjava.net/mstar/aggbug/5657.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-07 13:50 <a href="http://www.blogjava.net/mstar/archive/2005/06/07/5657.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The New Holy Trinity</title><link>http://www.blogjava.net/mstar/archive/2005/06/02/5448.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 02 Jun 2005 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/02/5448.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5448.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/02/5448.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5448.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5448.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;Dependency injection, annotations, and aspects 将成为今后一段时间J2EE领域里面技术层上的主流。所以应该成为我们小组的研究重点……<BR><BR>这三个东西是我在刚开始学习编程序时怎么也想不到的：原来程序也可以这么编！<BR>这三个东西有一个共同点，就是他们都是代码外面的代码，程序外面的程序。<BR><!--StartFragment -->&nbsp;
<H3>The New Holy Trinity</H3>
<P>No, this isn't a religious post. It's about an approach to developing applications. I'm not the only one advocating this approach, and I'm not the first to write about some of the ideas here. What I want to do in this post though is put all the pieces together in one place and boil the approach down to its essence to give you a simple way of thinking about it. So here it is:</P>
<BLOCKQUOTE>Dependency injection, annotations, and aspects are the three foundations on which the next generation of world-class POJO-based applications should be built. </BLOCKQUOTE>
<P align=center><IMG src="http://www.aspectprogrammer.org/images/trinity.jpg"> </P>
<P>At the centre of an application designed according to this principle are plain-old Java objects (POJOs). Each POJO should do one thing and one thing only. Each POJO should also <I>know</I> one thing and one thing only - how to do what it is that it does. This is the 'secret' that the the POJO hides from the rest of the application. So for example, a BankAccount POJO should know about the internal state of a bank account, and the operations that it supports. It should not know anything about the way any services it needs are created or deployed. It should not know anything about the authentication or authorization requirements that may be placed on it in a particular deployment scenario - or worse still, anything about the <I>implementation</I> of authenticaton and authorization services in the environment in which it is deployed. It should not know anything about account activity monitoring to detect potentially fradulent use. And so on. </P>
<P>I've written about this notion of a 1:1 mapping between distinct concepts in the design and constructs (types) in the implementation before in "<A href="http://www.aspectprogrammer.org/blogs/adrian/2004/05/the_ted_neward.html">AOP without the buzzwords</A>" (this essay was also updated and expanded to form the introduction for our book, <A href="http://www.awprofessional.com/title/0321245873">Eclipse AspectJ</A>). </P>
<P>To use some old software engineering terms, what's at the centre of applications designed according to these principles are highly cohesive POJOs. These POJOs are very loosely coupled to the rest of the application. In particular, if you look at the three sides of the triangle in the illustration, you'll see that <I>dependency injection</I> ensures the POJO remains unaware of any of the details of how its dependencies are instantiated and configured. DI shields the POJO from this information. <I>Aspects</I> ensure the POJO remains unaware of any crosscutting concerns in the application (such as the account activity monitoring concern in the example above). Aspects are what enable the POJO to focus on doing just one thing and to do it well. <I>Annotations</I> provide additional metadata about the POJO that facilitate easy integration with external services (such as transaction services). They ensure that the POJO remains unaware of any of the details of such services. Annotations coupled with aspects can do much more than this though - they also support the creation of domain specific abstractions that easily bring back to the programmer Abelman and Sussman's often missing third tool in the war against software complexity: the creation of "little languages" (see my post "<A href="http://www.aspectprogrammer.org/blogs/adrian/2004/12/a_beautiful_lan.html">A Beautiful Language</A>"). </P>
<P>This leads me to a really important point about this trinity of DI, aspects, and annotations. The three techniques are mutually self-reinforcing, the whole is much greater than the sum of the parts. </P>
<P>Dependency injection can be used to configure aspects. This is important since when you're building an application using the 1:1 principle, some of the POJOs in the centre of the triangle will themselves be aspects (in a language like AspectJ, this is perfectly natural). Anecdotal evidence from a number of projects across a number of organizations suggests that maybe about 10% of your "POJOs" will be aspects. Dependency injection can also be guided by annotations. You see an example of this in the EJB 3.0 specification (you may recall my own thoughts on the suitability of the chosen annotation name in that spec "<A href="http://www.aspectprogrammer.org/blogs/adrian/2004/05/ejb_30_whats_wr.html">What's wrong with @Inject?</A>", but that's not material to this discussion). Completing the circle, aspects can also be used to implement dependency injection scenarios. In general you're best leaving this job to an IoC container, but if you have DI requirements that go beyond the wiring of beans as they are instantiated by the container, aspects are a great solution. To give a couple of examples, in the <A href="http://www.awprofessional.com/title/0321245873">Eclipse AspectJ</A> book we use aspects to implement a 1:1 design for POJO persistence using Hibernate. We use dependency injection on a <I>per-request basis</I> to inject the <CODE>Session</CODE> object that DAOs use for communicating with Hibernate. A second example is the use of an annotation <CODE>@SpringConfigured(beanName="XXX")</CODE> on POJOs. It's easy to write an aspect (configured by DI to be <CODE>BeanFactoryAware</CODE> in Spring terms) that uses Spring to autowire any instance of an @SpringConfigured object when it is instantiated. This lets you separate instantiation and configuration (normally an IoC container like Spring does both for you) so that you can place instantiation under programmatic control, but still get configuration via DI. </P>
<P>Let's look at another really important way in which these techniques work together. Annotations on their own give you a way of specifying additional metadata for program elements. This is useful when you have tools or services that can understand those annotations and do something with them. Annotations coupled with aspects is a whole other ball game altogether. Aspects let you use annotations to easily implement declarative domain-specific languages (DSLs) as part of your application (or aspect library). This is a really big deal. Aspects can do this since they let you associate behaviour with join points relating to annotated elements. To give an example I've written about recently ("<A href="http://www.aspectprogrammer.org/blogs/adrian/2005/01/making_concurre.html">Making Concurrency a Little Bit Easier</A>"), you can easily write a set of annotations that form a declarative DSL for concurrent applications (the <I>domain</I> of this DSL is concurrency). Another example I'll write about in my next post: after attending Gregor Hohpe's talk at TSS yesterday on <A href="http://www.eaipatterns.com/">event-driven architectures</A> I quickly implemented an annotatation-based DSL ("@DSL") for simple event-driven applications. It took me less than an hour. That's pretty powerful (you can judge for yourself when you read the post). Annotations <I>plus</I> aspects let you write these @DSLs. </P>
<P>When you're using @DSLs like this in the construction of your application, you're going to want an aspect language that has rich support for join point matching based on annotations. There's a surprising amount to this, and you can see how AspectJ 5 handles it by reading the "Annotations" chapter of the <A href="http://dev.eclipse.org/viewcvs/indextech.cgi/%7Echeckout%7E/aspectj-home/doc/ajdk15notebook/index.html">AspectJ 5 Developer's Notebook</A>. </P>
<P>There's another way in which the combination of aspects and annotations are self-reinforcing too. If you are using an annotation-driven tool or service for a particular deployment of your application, you may need to annotate your POJOs with annotations specific to that tool or service. If you don't want to tie your POJOs to that annotation set (see "<A href="http://www.aspectprogrammer.org/blogs/adrian/2004/08/when_is_a_pojo.html">When is a POJO not a POJO?</A>") you can use the AspectJ 5 support for "declare annotation" to keep the annotations separate and maintain a loose coupling. You might also want to use declare annotation if a particular tool or service <I>requires</I> you to spread information you consider to be configuration information around your source code (in the form of annotations). Here an aspect using declare annotation can at least get that all back in one place for you. </P>
<P>If this all sounds a bit circular (DI of aspects, aspects to perform DI, annotations to control aspect behaviour, aspects to declare annotations,...) that's because it's meant too. The sides of the triangle all support each other and can be used together in many different ways to meet the needs of your particular situation. The whole is much greater than the sum of the parts. </P>
<P>In summary, I believe that dependency injection, annotations, and aspects are the three foundations on which the next generation of world-class POJO-based applications should be built. At the heart of such applications are POJOs that do one thing only and do it well. The triangle illustration provides a simple visual mnemonic to help you remember the principles. An application constructed according to these principles could be said to follow a "triangle design". You can even easily communicate your design intentions in good old ascii:</P><PRE>        /o\
	 -
	</PRE><img src ="http://www.blogjava.net/mstar/aggbug/5448.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-02 09:43 <a href="http://www.blogjava.net/mstar/archive/2005/06/02/5448.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Protecting the Domain Model</title><link>http://www.blogjava.net/mstar/archive/2005/06/02/5447.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 02 Jun 2005 01:34:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/02/5447.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5447.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/02/5447.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5447.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5447.html</trackback:ping><description><![CDATA[[转载]<BR><!--StartFragment -->&nbsp;
<P>In my previous blog post I found several reasons for using DTOs instead of using the domain model classes as transfer objects. I received some feedback and several people were saying that DTOs are a thing of the past and should be now considered an anti-pattern.</P>
<P>My main argument in favor of using DTOs was that they provide protection for the domain model and facilitate easier refactoring. The domain model needs to be protected from getting polluted with presentation related concerns. Refactoring would be easier if you could change the domain model classes without the client code being affected. This would be possible if the client was not dependent on the domain model classes directly. When using DTOs the dependency is not direct, the DTO classes provide some isolation. Effective refactoring is necessary in order to achieve a working domain model and not end up with an anemic one.</P>
<P>In this post I will discuss some other ways to protect the domain model and to facilitate effective refactoring. This time I am assuming that domain model classes are exposed to clients and DTOs are not used.</P>
<P><STRONG>Avoid Anemic Setters</STRONG></P>
<P>I learned the term "anemic setter" from the Spring forum's architecture discussions. An anemic setter is a setter that uses a primitive (or a String or some other JDK type) as it's parameter type and assigns the parameter value value to the domain object's field. In some cases anemic setters should be avoided. For example if your Customer object stores an address it probably should not expose setters for individually assigning the street address, the zip code and the country. Instead the address should be modeled as a separate Address object and the Customer should have a setter that takes the Address as a parameter. Or alternatively, the Address could be specified when the Customer is created. The latter is probably better if every Customer must have a valid Address. The Address class must also enforce that all it's component values are provided when instances of it are created.</P>
<P>This arrangement guarantees that the Customer state is never corrupted. A buggy or malicious client is not able to assign a partial address consisting of, for example, only the zip code. The anemic setters design relies that all clients, in all situations, use all three different setters when storing or modifying addresses.</P>
<P>The lack of anemic setters places some challenges for the data binding solution. A primitive binding framework might require that all fields coming from the UI (HTTP request or a Swing form) must be stored using anemic setters.</P>
<P><STRONG>Domain Logic Only</STRONG></P>
<P>The domain model should contain only logic from the core domain. Presentation logic should never find it's way to the domain model classes. So what is the proper place for presentation related functionality then? Here are two options:</P>
<UL>
<LI>Create separate model classes (MVC models, form objects)</LI></UL>
<UL>
<LI>Use AOP to ensure that domain model classes are only concerned with the core domain. Here is a great article about <A href="http://www.aspectprogrammer.org/blogs/adrian/2005/03/the_new_holy_tr.html">"next generation of world-class POJO-based applications"</A>.</LI></UL>
<P><STRONG>Unusable Methods with a Distributed Environment</STRONG></P>
<P>I discussed unusable methods in my <A href="http://api.blogs.com/the_catch_blog/2005/05/domain_objects_.html">previous blog entry</A>. I haven't figured out how to avoid them. AOP magic comes to my mind again here... Does anyone have suggestions?</P>
<P><STRONG>Evolving the Architecture</STRONG></P>
<P>My recipe for success follows here. </P>
<P>Start simple and use the domain model throughout the the system, including the presentation and other types of clients. This way you will be enjoying the benefits of the domain model everywhere. Your client code will appear to be speaking the domain specific language.</P>
<P>After this you need to keep your eyes open all the time. Look at your domain model:</P>
<UL>
<LI>Is it alive and rich with domain logic? </LI></UL>
<UL>
<LI>Has the domain logic leaked out of the domain model?</LI></UL>
<UL>
<LI>Has non-core functionality leaked into the domain model?</LI></UL>
<UL>
<LI>Is refactoring painful and hurting domain model evolution because you are&nbsp; worrying about breaking hard-to-change client code?</LI></UL>
<P>If these kind of signs are visible, it is time to refactor. Maybe some additional UI specific helper classes or model classes are necessary to host the presentation logic that was leaked into the domain classes. Or maybe you even<BR>need to introduce command classes (or DTOs) to isolate the domain model in some troubled area. You need to be alert all the time to be able to react quickly. Even better, you can refactor the architecture <EM>before</EM> adding anything that would lead to a problematic design.、<BR><BR>我认为也不必过于死板的非得隔离domain model曾。model仅仅是一个数据载体，一般人不会在其中加入logic。但是有些时候persistent层的model要的数据和domain层要得数据不一致，我看到有些人在domain层的model中加入一些东西，这样这个model即可以用在persistent层也可以用在domain层。这样是很方便，但是破坏了结构。在这种情况下，不如在建立一个persistent层的model用户装载数据。当然如果domain层的model在不做改变的情况下就可以被persisitent层使用，那也不一定非得建立一个一模一样的persistent层的model（当然如果你愿意，我也不拦你<IMG height=19 src="http://www.blogjava.net/Emoticons/regular_smile.gif" width=19 border=0>）。</P><img src ="http://www.blogjava.net/mstar/aggbug/5447.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-02 09:34 <a href="http://www.blogjava.net/mstar/archive/2005/06/02/5447.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JCA 1.5，第 2 部分: 工作管理和事务流入</title><link>http://www.blogjava.net/mstar/archive/2005/06/01/5388.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 01 Jun 2005 01:50:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/01/5388.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5388.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/01/5388.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5388.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5388.html</trackback:ping><description><![CDATA[<!--StartFragment --><FONT size=4><STRONG>&nbsp;<SPAN class=atitle2>同步、异步、周期以及事务地执行工作</SPAN></STRONG></FONT>&nbsp;<BR><!--StartFragment --> 
<BLOCKQUOTE><ABSTRACT-EXTENDED>David Currie 将继续他的由三部分组成的系列文章，介绍 Java 2 企业版（J2EE）连接器架构（JCA）最新版本中的增强和变化。在本文中，他将介绍新的 JCA 1.5 工作管理合约，该合约允许资源适配器利用应用服务器的某些功能来调度和处理工作。在 JCA 的另一个增强 —— 事务流入 —— 的支持下，企业信息系统可以在自己的事务中执行这项工作。 </ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>JCA 1.5 是 J2EE 连接器体系结构的最新版本，它包含许多重要的改进和一些重要增强。本文是介绍这些变化的由三部分组成的系列文章的第 2 部分，建立在介绍的生命周期管理特性的第 1 部分的基础上，本文介绍的是 JCA 新的工作管理合约。该合约允许资源适配器为延迟执行或定期执行的工作创建计时器，并允许计时器使用应用服务器的线程同步或异步执行处理。本文将描述事务流入支持如何在资源适配器导入到服务器的事务中进行这种处理，以及资源适配器随后如何控制事务的完成。</P>
<P>如果想在现有资源适配器中使用这项功能，或者正在考虑编写新的 JCA 1.5 资源适配器，那么本文是一份必不可少的读物。如果要编写使用资源适配器的应用程序，想了解更多幕后的情况，那么本文也会吸引您。</P>
<P><A name=IDALC2FB><SPAN class=atitle2>让工作完成</SPAN></A><BR>在本系列的第 1 部分中，我们介绍了 <CODE>ResourceAdapter</CODE> 接口，它提供了一种机制，能够在应用服务器内部为资源适配器提供生命周期。您可能还记得 <CODE>start</CODE> 方法被用来传递一个叫做 <CODE>BootstrapContext</CODE> 的对象。上次我们介绍了这个对象，但是 <CODE>BootstrapContext</CODE> 接口的三个方法才是工作管理和事务流入合约的关键，如清单 1 所示：</P><A name=IDACD2FB><B>清单 1. 用来得到三个工具对象的 <CODE>BootstrapContext</CODE> 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="65%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface BootstrapContext {

    WorkManager getWorkManager();
    XATerminator getXATerminator();
    Timer createTimer() throws UnavailableException;

}</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>WorkManager</CODE> 允许资源适配器对工作进行调度，在应用服务器线程上同步或异步执行调度。这个工作可以在资源导入的事务中执行，在这种情况下，<CODE>XATerminator</CODE> 有助于完成工作。<CODE>Timer</CODE> 负责延迟工作或定期工作的执行。本文将更深入地研究这三个类，并说明如何使用它们。</P>
<P><CODE>WorkManager</CODE> 接口提供了三套处理工作的方法（<CODE>doWork</CODE>、 <CODE>startWork</CODE> 和 <CODE>scheduleWork</CODE>)，如清单 2 所示：</P><A name=listing2><B>清单 2. 用来提交工作项目的<CODE>WorkManager</CODE> 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface WorkManager {
    
    void doWork(Work work) throws WorkException;
    void doWork(Work work, long startTimeout, ExecutionContext execContext,
            WorkListener workListener) throws WorkException;

    long startWork(Work work) throws WorkException;
    long startWork(Work work, long startTimeout, ExecutionContext execContext,
            WorkListener workListener) throws WorkException;

    void scheduleWork(Work work) throws WorkException;
    void scheduleWork(Work work, long startTimeout,
            ExecutionContext execContext, WorkListener workListener)
            throws WorkException;
    
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>每个方法接收的第一个参数，都是实现 <CODE>Work</CODE> 接口的对象的一个实例，如清单 3 所示：</P><A name=IDA5E2FB><B>清单 3. 资源适配器实现的 <CODE>Work</CODE> 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface Work extends Runnable {

    void release();
    
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>Work</CODE> 接口扩展了 <CODE>Runnable</CODE> 接口，您应当像直接进行 Java 线程编程时所做的那样，实现<CODE>run</CODE> 方法中执行的工作。您很快就会看到 <CODE>release</CODE> 方法发挥其作用的地方。</P>
<P><CODE>WorkManager</CODE> 上的 <CODE>doWork</CODE> 方法可以让一些工作同步执行、一直受阻塞或者直到某些工作完成才执行。这看起来可能不是特别有用——这不就是直接调用 <CODE>run</CODE> 方法时发生的事情吗？并不完全如此。首先，它让应用程序服务器说明现在不是做这项工作的恰当时候。例如，如果在 <CODE>ResourceAdapter</CODE><CODE>start</CODE> 方法的范围内调用 <CODE>doWork</CODE>，那么您可能发现它将抛出 <CODE>WorkRejectedException</CODE> 异常。应当尽快从这个方法返回，如果可能的话，应当把工作安排成异步处理。</P>
<P>第二，如果应用服务器特别繁忙，那么它可能会推迟这项工作的启动。可以用第 2 个<CODE>startTimeout</CODE> 参数指明资源适配器准备为工作启动等候多长时间。如果应用服务器没能在这个时间内启动工作，那么就会抛出 <CODE>WorkRejectedException</CODE> 异常。<CODE>WorkManager</CODE> 接口定义了常量 <CODE>IMMEDIATE</CODE> 和 <CODE>INDEFINITE</CODE>，它们允许资源适配器指明自己根本不准备等候或者准备一直等候下去。</P>
<P>第三，正如下一节解释的，有可能让工作片断在资源适配器导入的事务上下文中执行，而不是在与当前线程关联的上下文中执行。这正是第 3 个参数的用途，该参数是一个可选的 <CODE>ExecutionContext</CODE>。</P>
<P>最后，使用 <CODE>doWork</CODE> 方法让应用服务器对资源适配器执行的工作拥有更多控制。在关闭应用服务器时，如果资源适配器夹在一个漫长的、复杂的操作中间，那么服务器不需要等待资源适配器完成，或者被清理干净。相反，服务器可以通过调用 <CODE>Work</CODE> 对象的 <CODE>release</CODE> 方法，给资源适配器发信号。然后资源适配器应当尽快完成处理。清单 4 显示了 <CODE>release</CODE> 方法使用的一个示例：</P><A name=listing4><B>清单 4. <CODE>Work</CODE> 对象的示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public class ExampleWork implements Work {

    private volatile boolean _released;

    void run() {
        for (int i = 0; i &lt; 100; i++) {
            if (_released) break;
            // Do something
        }
    }

    void release() {
        _released = true;
    }
    
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意清单 4 中使用的关键字 <CODE>volatile</CODE>。在 <CODE>run</CODE> 方法正进行其处理的同时，会在一个独立的线程上调用 <CODE>release</CODE> 方法，<CODE>volatile</CODE> 修饰符确保 <CODE>run</CODE> 方法能够看到更新的字段。</P>
<P><CODE>doWork</CODE> 方法也可能失败，抛出一个名称古怪的 <CODE>WorkCompletedException</CODE> 异常。这个异常是在将 <CODE>run</CODE> 方法分配给一个线程时抛出的，但这个异常或者是上下文设置失败，或者是通过抛出运行时异常退出方法。<CODE>doWork</CODE> 方法会提供一个错误代码，表示这些故障发生的路径，还把问题原因作为链接的异常提供。</P>
<P><A name=IDA2ZBGB><SPAN class=atitle2>为什么等待？</SPAN></A><BR>您已经看到 <CODE>doWork</CODE> 方法允许您在调用线程阻塞的同时执行工作。但是如果您不想等待 —— 也就是说，如果您不想同步执行工作，该怎么办呢？在 Java 2 平台标准版本（J2SE）环境中，可以使用多线程实现这一目标。但是，J2EE 规范让应用程序服务器使用 Java 2 安全管理器防止应用程序离开自己的线程。如果应用服务器的这部分功能是通过 EJB 池或 servlet 池来提供并发性，那么这样做是合理的。服务器也可能想把各种形式的上下文都关联到一个线程上，因为产生新线程时，上下文可能会丢失。最后，正如我以前讨论过的，不受服务器控制的线程会造成有序关机很难实现。</P>
<P>虽然可以通过使用安全策略，逐个案例地克服这个限制，但是 <CODE>WorkManager</CODE> 上的 <CODE>startWork</CODE> 和 <CODE>scheduleWork</CODE> 方法可以让资源适配器异步地处理工作，同时确保应用程序服务器仍然在控制之下。<CODE>startWork</CODE> 方法会一直等候，直到工作片断开始执行，但不用等到工作结束。所以，如果调用器需要知道工作是否已经得以执行，但是不需要等到工作完成，那么可以使用这种方法。相比之下，只要该工作调度被接受，<CODE>scheduleWork</CODE> 方法就立即返回。在这种情况下，并不能确保工作真的被执行。</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-jca2/index.html?ca=dwcn-newsletter-java#listing2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 2</A> 中的 <CODE>WorkManager</CODE> 方法的第 4 个参数（<CODE>WorkListener</CODE>）在用于这些异步方法时最有用。清单 5 显示了这个接口：</P><A name=IDAK1BGB><B>清单 5. 接收事件通知的 <CODE>WorkListener</CODE> 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface WorkListener {

    void workAccepted(WorkEvent e);
    void workRejected(WorkEvent e);
    void workStarted(WorkEvent e);
    void workCompleted(WorkEvent e);
    
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>资源适配器能够有选择地传递一个监听器，当工作的项目通过接受、启动或完成这几个状态（失败的情况下则是拒绝）传递过来时，监听器会得到通知。在这个监听器已经完成其工作项目，或者出现故障要重新安排工作项目时，可以用它将通知发送给工作的发起者。<CODE>WorkAdapter</CODE> 类也包含在内，它提供了所有方法的默认实现，因此，子类只需覆盖自己感兴趣的方法即可。</P>
<P><CODE>WorkListener</CODE> 的每个方法都采用 <CODE>WorkEvent</CODE> 对象作为参数，如清单 6 所示：</P><A name=IDAF2BGB><B>清单 6. <CODE>WorkEvent</CODE> 类上的附加方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public class WorkEvent extends EventObject {

    ...

    public int getType() { ... }
    public Work getWork() { ... }
    public long getStartDuration() { ... }
    public WorkException getException() { ... }
 
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>除了通常的事件方法之外，<CODE>WorkEvent</CODE> 类还为这些类型的事件 （接受、拒绝、启动或完成）以及有问题的工作项目提供了存取器。这使您能够用一个（多线程的）监听器负责多个工作提交。还有一些方法可以返回启动工作所花费的时间，而且，在出现 <CODE>workRejected</CODE> 或 <CODE>workCompleted</CODE> 时，返回可能发生的对应的 <CODE>WorkRejectedException</CODE> 或 <CODE>WorkCompletedException</CODE> 异常。</P>
<P>图 1 显示了通过 <CODE>Work</CODE> 对象传递的状态。</P>
<P><A name=figure1><B>图 1. Work 对象的状态</B></A><BR><IMG height=200 alt="Work 对象的状态" src="http://www-128.ibm.com/developerworks/cn/java/j-jca2/workstates.gif" width=600 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>沿着图 1 的底部，有三种提交方法，从上到下的点线表示具体的方法返回的生命周期中的时间点。</P>
<P><A name=IDA13BGB><SPAN class=atitle2>可以推迟到明天的事为什么要现在做？</SPAN></A><BR><CODE>doWork</CODE>、<CODE>startWork</CODE> 和 <CODE>scheduleWork</CODE> 方法可以都立即向 <CODE>WorkManager</CODE> 提交任务。在 <CODE>WorkManager</CODE> 执行提交之前，可能会发生延迟，但是调用者只能控制最大延迟（用启动超时）。那么如果想让任务晚些而不是立即处理，该怎么办呢？<CODE>scheduleWork</CODE> 方法只允许将工作安排到另一个线程，而不是安排到时间轴上的以后某个时间点上。</P>
<P>这正是 <CODE>BootstrapContext</CODE> 接口的第 3 个方法发挥其作用的地方。<CODE>createTimer</CODE> 方法使资源适配器能够得到 <CODE>java.util.Timer</CODE> 类的实例。这个类从 1.3 版开始就已经成为标准 Java 库的一部分，如清单 7 所示：</P><A name=IDAH5BGB><B>清单 7. <CODE>Timer</CODE> 类的方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public class Timer  {
 
    public void schedule(TimerTask task, long delay) { ... }
    public void schedule(TimerTask task, Date time) { ... }

    public void schedule(TimerTask task, Date firstTime, long period) { ... }
    public void schedule(TimerTask task, long delay, long period) { ... }
    public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { ... }
    public void scheduleAtFixedRate(TimerTask task, long delay, long period) { ... }

    public void cancel() { ... }
 
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>可以用 <CODE>java.util.Timer</CODE> 类的前两个方法把任务安排在指定延迟之后或指定日期和时间发生。余下的 4 个调度方法还有额外的 <CODE>period</CODE> 参数。可以用它们对那些初次运行之后需要按照常规间隔发生的事件进行调度，使用周期参数指定时间间隔。<CODE>schedule</CODE> 和 <CODE>scheduleAtFixedRate</CODE> 方法是不同的方法，因为这些操作的计时无法保证；像垃圾搜集这样的操作可能会造成任务推迟执行。如果任务被延迟，那么 <CODE>schedule</CODE> 方法在运行下一个任务之前仍然会等候一个完整的周期。但是，<CODE>scheduleAtFixedRate</CODE> 方法是在前一个任务 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">应当</I> 运行的固定周期之后运行下一个任务。所以，如果任务之间的时间对您非常很重要，那么请使用 <CODE>schedule</CODE>。如果绝对时间或累积时间很重要，那么请使用 <CODE>scheduleAtFixedRate</CODE>。</P>
<P>每个 <CODE>schedule</CODE> 方法都采用一个扩展了 <CODE>TimerTask</CODE> 类的对象作为自己的第一个参数。同使用 <CODE>Work</CODE> 接口时一样，这个类扩展了 <CODE>Runnable</CODE> ，而且 <CODE>Timer</CODE> 会在适当的时间调用 <CODE>run</CODE> 方法。<CODE>TimerTask</CODE> 类有一个 <CODE>cancel</CODE> 方法，可以用它取消对任务的后续调用。或者，也可以调用 <CODE>Timer</CODE> 上的 <CODE>cancel</CODE> 方法来取消目前安排的所有任务。<CODE>scheduledExecutionTime</CODE> 方法允许 <CODE>run</CODE> 方法将当前实际调用时间和它应当被调用的时间进行比较。</P>
<P>虽然 <CODE>TimerTask</CODE> 的 <CODE>run</CODE> 方法是在新线程中调用 ，但是这个线程是 JVM 已经分配的线程，处于应用服务器的控制之外。如果您想让资源适配器进行严肃的处理，那么在这个时候，应当用 <CODE>WorkManager</CODE> 切换到应用服务器的线程。清单 8 显示的示例采用了这一良好实践来调度工作，从当前时间开始每分钟执行一次：</P><A name=listing8><B>清单 8. 组合使用 <CODE>Timer</CODE> 和 <CODE>WorkManager</CODE></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>final WorkManager workManager = context.getWorkManager();
final Timer timer = context.createTimer();

timer.scheduleAtFixedRate(new TimerTask() {
    public void run() {
        workManager.scheduleWork(new ExampleWork());
    }
}, 0, 60 * 1000);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=IDAJDCGB><SPAN class=atitle2>JCA WorkManager 和 Timer 的替代方案</SPAN></A><BR>正如前一小节提到过的，在 JCA 中，使用 <CODE>Timer</CODE> 有一个问题：执行 <CODE>TimerTask</CODE> 的线程不在应用服务器的控制之下。因为 <CODE>Timer</CODE> 是类而不是接口，而且线程实际是在类的构造函数中产生的， 所以应用服务器不能修改这种行为。允许应用服务器实现这一目的一个替代方案就是使用由 IBM 和 BEA 联合开发的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Timer and Work Manager for Application Servers</I> 规范的接口（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca2/index.html?ca=dwcn-newsletter-java#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>） 。</P>
<P>在使用这个规范的接口时，会从 Java 名称与目录服务接口（JNDI）得到一个 <CODE>TimerManager</CODE> 实现。可以通过给管理器安排了一个 <CODE>TimerListener</CODE> 实现来返回一个 <CODE>Timer</CODE> （是一个<CODE>commonj.timers.Timer</CODE> 而不是 <CODE>java.util.Timer</CODE>）。在安排好的时间上会调用 <CODE>TimerListener</CODE>，可以用 <CODE>Timer</CODE> 执行诸如取消预定操作之类的操作。</P>
<P>顾名思义，这个规范还提供了 JCA 工作管理的一个替代。这里的接口与 JCA 的那些接口很相似，除了初始的 <CODE>commmonj.work.WorkManager</CODE> 是从 JNDI 得到的。这个合约提供的附加行为还包括：阻塞到一个或全部预定工作完成的能力、在远程 JVM 上执行可以序列化的工作的可能性。与 commonj <CODE>Timer</CODE> 一样，commonj <CODE>WorkManager</CODE> 的应用不限于资源适配器。任何服务器端 J2EE 组件都可以使用这项功能。</P>
<P>清单 9 显示了为了使用 commonj 的类而被重写的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca2/index.html?ca=dwcn-newsletter-java#listing8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 8</A> 的示例：</P><A name=IDAUFCGB><B>清单 9. 使用 commonj 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>final InitialContext context = new InitialContext();
final WorkManager workManager = (WorkManager) 
        context.lookup("java:comp/env/wm/MyWorkManager");
final TimerManager timerManager = (TimerManager)
	context.lookup("java:comp/env/timer/MyTimer");

timerManager.scheduleAtFixedRate(new TimerListener() {
    public void timerExpired(final Timer timer) {
        try {
            workManager.schedule(new ExampleCommonjWork());
        } catch (final WorkException exception) {
        }
    }
}, 0, 60 * 1000);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>ExampleCommonjWork</CODE> 类与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca2/index.html?ca=dwcn-newsletter-java#listing4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 4</A> 的 <CODE>ExampleWork</CODE> 相同，此外，它还实现了 commonj <CODE>Work</CODE> 接口要求的额外的 <CODE>isDaemon</CODE> 方法。如果工作长期存在，那么该方法应当返回 <CODE>true</CODE>。</P>
<P><A name=IDAVGCGB><SPAN class=atitle2>导入事务并完成事务</SPAN></A><BR>在 JCA 1.5 之前，应用服务器总是充当事务的协调者。应用程序负责器负责启动事务，而且在每个资源管理器通过 JCA 连接管理器登记了自己的 <CODE>XAResource</CODE> 之后，还通过一起提交或回滚所有资源来协调事务的完成。JCA 1.5 的事务流入合约允许企业信息系统（EIS）启动和完成事务，充当事务的协调者。这样 EIS 就能通过资源适配器把事务导入应用服务器，并在事务范围内在服务器上执行工作。例如，它可能使用 <CODE>Supports</CODE> 的容器管理器事务属性来调用消息驱动 bean（MDB）。这样，MDB 方法执行的工作，包括对其它 EJB 的调用，就会成为事务的一部分。</P>
<P>资源适配器通过第 3 个 <CODE>ExecutionContext</CODE> 参数导入事务，在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca2/index.html?ca=dwcn-newsletter-java#listing2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 2</A> 中可以看到它被传递给 <CODE>WorkManager</CODE>。正如在清单 10 中可以看到的，这个类拥有设置事务 ID （XID） 和事务超时的方法：</P><A name=IDASHCGB><B>清单 10. 传递给 <CODE>WorkManager</CODE> 的 <CODE>ExecutionContext</CODE> 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public class ExecutionContext {

    public ExecutionContext() { ... }
    public void setXid(Xid xid) { ... }
    public Xid getXid() { ... }
    public void setTransactionTimeout(long timeout)
        throws NotSupportedException { ... }
    public long getTransactionTimeout() { ... }    
    
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>XID 惟一地标识事务，它由三部分构成：格式标识符、事务界定符和分支界定符。如果 EIS 还没有事务的 XID，那么就必须根据 XA 规范（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca2/index.html?ca=dwcn-newsletter-java#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）构建一个 XID。然后应用服务器会在调用工作对象的 <CODE>run</CODE> 方法之前把这个事务与执行线程关联起来。</P>
<P>把事务导入应用服务器之后，接下来就由资源适配器负责把属于这个事务的事件通知给服务器。具体地说，它必须把事务完成通知给应用服务器。它是通过清单 11 中的 <CODE>XATerminator</CODE> 接口做到这一点的，可以从 <CODE>BootstrapContext</CODE> 中得到它的实现：</P><A name=IDAZICGB><B>清单 11. 用于事务完成的 <CODE>XATerminator</CODE> 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface XATerminator {

    void commit(Xid xid, boolean onePhase) throws XAException;
    void forget(Xid xid) throws XAException;
    int prepare(Xid xid) throws XAException;
    Xid[] recover(int flag) throws XAException;
    void rollback(Xid xid) throws XAException;
    
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>XATerminator</CODE> 接口的方法与 <CODE>XAResource</CODE> 上的方法对应，只是在这个例子中，资源适配器需要调用应用程序服务器。典型情况下，如果事务中包含不止一个资源，那么资源适配器会调用 <CODE>XATerminator</CODE> 的 <CODE>prepare</CODE> 方法，传递与 <CODE>ExecutionContext</CODE> 中传递的 Xid 相同的 <CODE>Xid</CODE>。如果所有的资源都返回 <CODE>XA_OK</CODE> （或者 <CODE>XA_RDONLY</CODE>），那么资源适配器会接着调用 <CODE>commit</CODE>；否则就会调用 <CODE>rollback</CODE>。</P>
<P><A name=IDAOKCGB><SPAN class=atitle2>结束语</SPAN></A><BR>本文介绍了如何用 <CODE>WorkManager</CODE> 接口对工作进行调度，以便在应用程序的控制下处理这些工作。您已经看到如何用 <CODE>Timer</CODE> 的实例在以后某个时间执行工作或定期执行工作，还了解了使用 JCA 提供的对象的 commonj 替代方案。您已经看到资源适配器如何在自己导入到应用服务器的事务中执行工作，并用 <CODE>XATerminator</CODE> 接口控制事务的完成。在本系列的第 3 篇也是最后一篇文章中，我将介绍 JCA 1.5 消息流入合约，它比较出名的地方是对消息驱动 bean 的支持。</P><img src ="http://www.blogjava.net/mstar/aggbug/5388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-01 09:50 <a href="http://www.blogjava.net/mstar/archive/2005/06/01/5388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JCA 1.5，第 1 部分: 优化和生命周期管理</title><link>http://www.blogjava.net/mstar/archive/2005/06/01/5387.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 01 Jun 2005 01:48:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/01/5387.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5387.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/01/5387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5387.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5387.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;
<BLOCKQUOTE><ABSTRACT-EXTENDED>本系列共三部分，在第一部分中，Java 开发人员 David Currie 介绍了 Java 2 Enterprise Edition (J2EE) Connector Architecture (JCA) 1.5 中所作的一些优化，它们可以加快现有或新外部资源适配器的运行速度。他还分析了一些使资源适配器焕发生机的新功能。若想提高现有资源适配器的性能或要加入新的功能，或者正在考虑编写新的 JCA 1.5 资源适配器，那么本系列是必不可少的。若要编写使用资源适配器的应用程序，并想知道更多的幕后情况，那么本系列也值得一读。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>JCA 1.5 是 J2EE Connector Architecture 的最新版本，它包含了许多重要的增强和新增功能。讨论这些变化的系列文章分为三个部分，本文是第一部分，将介绍该版本中所作的一些优化，它们可以加快那些使用出站资源适配器的应用程序，特别是那些使用事务的应用程序的运行速度。然后介绍对生命周期管理契约所作的扩展，它们可以防止应用程序的连接断开，并使资源适配器焕发新生。这将为第 2、3 部分奠定基础。第 2 部分讨论新的工作管理和事务流入契约，第 3 部分探讨期待已久的消息流入契约，其更常见的叫法是<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">消息驱动的 bean</I> (MDB) 支持。</P>
<P>本文假定读者对 JCA 连接管理契约有一定的了解。原来 1.0 规范中的所有内容仍然适用，因此如果刚刚接触 JCA，建议先看一看 Willy Farrell 的入门教程（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca1/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。</P>
<P><A name=IDAUSOJB><SPAN class=atitle2>连接加快</SPAN></A><BR>JCA 规范的新版本不会使应用程序与后端系统之间的连接变得更快，但 JCA 1.5 引入了两组接口，可以加快使用连接的应用程序的运行。第一组接口解除了以前 JCA 版本中应用服务器管理连接句柄方式的限制。</P>
<P>许多读者都知道，J2EE 支持两种连接使用模式，本文称之为<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> get-use-close </I>和 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">cached-handle</I>。对这些模式的进一步分析有助于理解 JCA 1.5 在这方面所带来的性能改善。</P>
<P><A name=IDAATOJB><SPAN class=atitle3>get-use-close </SPAN></A><BR>在 get-use-close 模式中，应用程序在需要新连接时，总是先获取新连接，然后使用，然后再关闭它，如清单 1 所示。（为清楚起见，本文没有在示例清单中加入异常处理逻辑。）</P><A name=listing1><B>清单 1. 使用 get-use-close 模式的一个 Enterprise JavaBean (EJB) </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public class GetUseCloseEJB implements SessionBean {

    ...

    public void businessMethod() {

       InitialContext context = new InitialContext();
       DataSource dataSource = (DataSource)context.lookup("java:comp/env/jdbc/mydatasource");
       Connection connection = datasource.getConnection();

       ...

       connection.close();

    }

}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>get-use-close 模式看起来效率不高，但是应用服务器实现的连接池可降低“获得”操作的成本。此外，因为应用程序只在需要时才保留连接，所以应用程序的不同实例或者部分可以重复使用该连接，从而降低了总的资源占用，如图 1 所示。</P>
<P><A name=figure1><B>图 1. 使用中的 get-use-close 模式</B></A><BR><IMG height=444 alt="使用中的 get-use-close 模式" src="http://www-128.ibm.com/developerworks/cn/java/j-jca1/getuseclose.gif" width=495 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>如图 1 所示，每当 bean 方法调用 <CODE>getConnection</CODE> 时，连接管理器都会重复使用池中托管的连接，并只创建一个新连接句柄。当连接句柄通知连接管理器它已关闭时，托管的连接就被清除并返回池中。图 1 中的绿色线条表示托管连接与应用程序的这个实例相关联的时间。</P>
<P><A name=IDADUOJB><SPAN class=atitle3>cached-handle</SPAN></A><BR>在 cached-handle 模式中，应用程序在一开始是获得连接，然后在一个实例字段中缓存对它的引用，如清单 2 所示。</P><A name=IDAKUOJB><B>清单 2. cached-handle 模式</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public class CachedHandleEJB implements SessionBean {

    private Connection _connection;

    ...

    public void ejbCreate() {

       InitialContext context = new InitialContext();
       DataSource dataSource = (DataSource)context.lookup("java:comp/env/jdbc/mydatasource");
       _connection = datasource.getConnection();

    }

    public void businessMethod() {

       ...

    }

}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>应用程序开发人员通常都使用 cached-handle 方法，因为他们认为这样应用程序的性能会更好。但是因为 get-use-close 模式中连接池的作用，这两种使用模式的性能差别一般来说是很小的。尽管 cached-handle 模式可使业务方法中的逻辑更简单，但是需要额外的逻辑以便在钝化（passivation）时关闭连接，并在激活时重建连接。当容器毁坏 bean 时（如由于方法生成了一个运行时异常），还有可能留下打开的连接。</P>
<P>cached-handle 模式的最大问题是当 bean 或者 servlet 的一个实例使用该连接时，其他实例就不能使用它——因此连接数最多只会有实例那么多。如图 2 所示。 </P>
<P><A name=figure2><B>图 2. 使用中的 cached-handle 模式</B></A><BR><IMG height=342 alt="使用中的 cached-handle 模式" src="http://www-128.ibm.com/developerworks/cn/java/j-jca1/cachedhandle.gif" width=495 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>从图 2 中可以看到，创建 EJB 时（从绿色线条开始），托管的连接与连接句柄关联，并且这个 bean 实例以外的任何其他对象都不能使用这个连接。（绿色线条无限延伸。）</P>
<P>针对这种情况，JCA 1.5 规范引入了两个新的接口，如清单 3 所示。</P><A name=listing3><B>清单 3. 解除关联（dissociation）和惰性关联（lazy association）接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface DissociatableManagedConnection {

    void dissociateConnections() throws ResourceException;

}

public interface LazyAssociatableConnectionManager {

    void associateConnection(Object connection,
                             ManagedConnectionFactory mcf,
                             ConnectionRequestInfo cxReqInfo)
            throws ResourceException

}</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=IDALVOJB><SPAN class=atitle3>解除关联</SPAN></A><BR>资源适配器的托管连接实现第一个接口—— <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">解除关联</I> 接口——以向连接管理器表明适配器支持这种优化。当连接暂时超出范围时（即当 bean 或者 servlet 方法退出时），如果连接管理器支持这种优化的话，它就可以解除由应用程序使用的连接句柄与表示物理资源的托管连接的关联。这使托管连接可以返回连接池，为应用程序的其他部分所使用。</P>
<P><A name=IDAVVOJB><SPAN class=atitle3>惰性关联</SPAN></A><BR>这种优化使用 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">惰性关联</I>，而不是在下次调用方法时重新将连接句柄与托管连接关联。如果方法没有使用连接，或者它只调用连接句柄上不需要访问后端的简单方法，那么托管连接未必会从池中移出。相反，当连接句柄确定它不需要与一个托管连接重新关联时，它就可以将连接管理器强制转换为 <CODE>LazyAssociatableConnectionManager</CODE> 并调用 <CODE>associateConnection</CODE> 方法。该方法以连接句柄为第一个参数，然后是托管连接厂，以及对 <CODE>allocateConnection</CODE> 的第一次调用时传递的请求信息。然后连接管理器从池中找到另一个合适的托管连接，并使用这个托管连接的 <CODE>associateConnection</CODE> 方法将它与连接句柄关联。</P>
<P>图 3 显示了这种优化对 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca1/#figure2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">图 2</A> 中的 cached-handle 用法的效果。</P>
<P><A name=figure3><B>图 3. 惰性关联降低资源用量</B></A><BR><IMG height=417 alt=惰性关联降低资源用量 src="http://www-128.ibm.com/developerworks/cn/java/j-jca1/lazyassociation.gif" width=495 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>图 3 中虚线箭头表示方法完成时 EJB 容器对连接管理器的通知。这时，托管的连接与连接句柄解除关联，只有当方法试图使用这个句柄时，托管连接才会重新关联。短的绿色线条显示托管连接现在绑定到 EJB 上，以缩短时间并可以在别的地方重新使用。</P>
<P><A name=IDADXOJB><SPAN class=atitle2>征募，还是不征募</SPAN></A><BR>大家都知道事务代价很高——特别是 XA（全局）事务。这使得让事务不执行非必需的工作变得很重要。JCA 1.5 中的另外两个新接口防止了 <CODE>XAResource</CODE> 对象的不必要征募。</P>
<P>我们现在来更详细地分析一下这种增强所针对的问题。假定取用 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca1/#listing1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 1</A> 中的 EJB，并用容器托管的事务部署它，将业务方法的事务属性设定为 <CODE>RequiresNew</CODE>。调用这个方法时便开始一个新的事务。创建连接时，连接管理器不知道如何使用它，因此它必须从关联的托管连接获得一个 <CODE>XAResource</CODE>，并在事务中征募它。连接可能只用于查询数据库或者根本不被使用。但是连接管理器必须征募连接，以备插入或者更新操作。这意味着，资源适配器至少必须进行开始、提交或者回滚，并结束流程返回后端。图 4 展示了这样的事务流程。</P>
<P><A name=figure4><B>图 4. 急切事务征募</B></A><BR><IMG height=345 alt=急切事务征募 src="http://www-128.ibm.com/developerworks/cn/java/j-jca1/eagerenlist.gif" width=579 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>在图 4 中可以看到，获得连接后，<CODE>XAResource</CODE> 立即征募到了事务中（即接收一个开始流程）。这意味着当事务方法结束时，流程需要在资源处结束，即使方法没有在事务中使用连接。</P>
<P>如果在事务中涉及另一项资源，就会强制进行不必要的两阶段提交，导致额外的准备流程。这里的惟一可取之处是资源管理器仍然可以从准备调用中返回 <CODE>XA_RDONLY</CODE> （用于“read only”）以表明它实际没有做任何工作。事务管理器不需要在那个资源管理器处完成调用，并且如果在事务中只有一个资源管理器真正做了工作，那么事务管理器也许可以避免日志文件中的惰性写操作。</P>
<P><A name=IDAYY1JB><SPAN class=atitle3>惰性征募</SPAN></A><BR>现在您应当认识到除非绝对需要，否则不要在事务中征募。JCA 1.5 规范提供了一个解决方案：<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">惰性征募</I>。清单 4 显示了支持这各种优化所引入的两个接口。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=IDACZ1JB><B>WebSphere 为 JCA 1.5 打下基础</B></A><BR>IBM WebSphere 应用服务器 V5 用 JCA 1.0 的一个扩展克服了 cached-handle 连接模式的缺点，它称为 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">智能连接句柄（smart connection handle）</I>。JCA 1.5 规范通过对<A href="http://www-128.ibm.com/developerworks/cn/java/j-jca1/#listing3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 3</A> 中的两个接口的修改吸取了这种扩展。</P>
<P>JCA 1.5 在其惰性征募支持中还采用了 WebSphere Application Server V5 的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">延迟事务征募</I> 扩展。</P>
<P>请阅读 developerWorks 中 Kevin Kelle 等人撰写的文章以了解更多的 WebSphere 扩展（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jca1/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。</P></TD></TR></TBODY></TABLE></P><A name=IDAZZ1JB><B>清单 4. 惰性征募的接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="65%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface LazyEnlistableManagedConnection {

}

public interface LazyEnlistableConnectionManager {

    void lazyEnlist(ManagedConnection mc)
            throws ResourceException;

}</CODE></PRE></TD></TR></TBODY></TABLE>
<P><CODE>LazyEnlistableManagedConnection</CODE> 接口是由托管连接实现的标记接口，用以向连接管理器表明在事务中创建新的连接或者在连接已经存在的情况下开始一个新的事务时，它不需要将托管连接急切征募到现有事务中。如果连接句柄准备执行应当是任一事务中的一部分的某些工作，并且它的托管连接还没有征募，那么应当确定连接管理器是否实施了 <CODE>LazyEnlistableConnectionManager</CODE> 接口。如果实施了，那么它应当调用传递托管连接的 <CODE>lazyEnlist</CODE> 方法。这个方法不返回任何结果，但是如果调用线程关联了一个事务，那么这时就征募托管连接的 <CODE>XAResource</CODE>。如果连接没有被征募，那么它需要在后面每一项工作之前再次调用 <CODE>lazyEnlist</CODE>，以检查在上次调用这个方法之后，是不是没有启动过事务。</P>
<P>图 5 显示了这个新的事件序列。</P>
<P><A name=figure5><B>图 5. 惰性事务征募</B></A><BR><IMG height=364 alt=惰性事务征募 src="http://www-128.ibm.com/developerworks/cn/java/j-jca1/lazyenlist.gif" width=575 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>在图 5 中可以看到，在获得连接时，<CODE>XAResource</CODE> 不再急切征募。相反，连接管理器会等待，直到连接用 <CODE>lazyEnlist</CODE> 调用表明它要完成一些事务工作时，才会征募 <CODE>XAResource</CODE>。</P>
<P><A name=IDAT11JB><SPAN class=atitle2>什么时候出现错误</SPAN></A><BR>JCA 规范的第一个版本提供了一种让资源适配器在连接出现严重错误时通知连接管理器的机制。这是 <CODE>ConnectionEventListener</CODE> 接口的 <CODE>connectionErrorOccurred</CODE> 方法。收到这个通知后，连接管理器就会毁环发送事件的托管连接，这样就不会再使用它。这些都是不错的。但是，如果到后端的连接丢失了，那么池中的许多托管连接也很有可能不能再使用。</P>
<P>针对这个问题，JCA 1.5 以 <CODE>ValidatingManagedConnectionFactory</CODE> 接口的方式引入了一种雅洁的解决方案，如清单 5 所示。</P><A name=IDAI21JB><B>清单 5. 用于确定无效连接的接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface ValidatingManagedConnectionFactory {

    Set getInvalidConnections(Set connectionSet)
            throws ResourceException;

}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>由托管连接厂实现的 <CODE>ValidatingManagedConnectionFactory</CODE> 接口包含一个方法——<CODE>getInvalidConnections</CODE>——它以一组托管连接为参数，返回资源适配器认为无效的一个子集。资源适配器的验证可以采取任何形式，不过通常涉及到对后端的某种“ping”操作，以测试连接。然后连接管理器在资源适配器指示连接错误时调用这个方法，甚至定期调用该方法，以便从池中排除坏掉的连接。</P>
<P><A name=IDA021JB><SPAN class=atitle2>开始和结束</SPAN></A><BR>最初的 JCA 版本为托管连接及其相关联的连接句柄提供了详细的生命周期模式，但是它没有为资源适配器提供这种概念。只有当创建了托管连接厂后，部署的资源适配器才知道它的存在。JCA 1.5 中引入的 <CODE>ResourceAdapter</CODE> 接口对此进行了纠正，如清单 6 所示。</P><A name=IDAF31JB><B>清单 6. 新资源适配器接口的生命周期方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface ResourceAdapter {

    void start(BootstrapContext ctx)
            throws ResourceAdapterInternalException;

    void stop();

    ...

}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>资源适配器可以在其部署描述符（ra.xml）的 <CODE>resourceadapter-class</CODE> 元素中给出实现这个接口的类的名称。除了实现 <CODE>ResourceAdapter</CODE> 接口，这个类可以通过 JavaBean 样式支持某些属性。与托管连接厂一样，在部署描述符中可以声明这些属性及其默认值，如清单 7 所示。在安装了资源适配器后，应用服务器将允许管理员覆盖这些默认值。</P><A name=IDAX31JB><B>清单 7. 资源适配器部署描述符</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>&lt;connector&gt;
  ...
  &lt;resourceadapter&gt;
    &lt;resourceadapter-class&gt;
      example.ExampleResourceAdapterImpl
    &lt;/resourceadapter-class&gt;
    &lt;config-property&gt;
      &lt;config-property-name&gt;ServerName&lt;/config-property-name&gt;
      &lt;config-property-type&gt;java.lang.String&lt;/config-property-type&gt;
      &lt;config-property-value&gt;MyServer&lt;/config-property-value&gt;
    &lt;/config-property&gt;
    &lt;config-property&gt;
      &lt;config-property-name&gt;PortNumber&lt;/config-property-name&gt;
      &lt;config-property-type&gt;java.lang.String&lt;/config-property-type&gt;
      &lt;config-property-value&gt;1976&lt;/config-property-value&gt;
    &lt;/config-property&gt;
  &lt;/resourceadapter&gt;
  ...
&lt;/connector&gt;</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在启动时，应用服务器会创建在部署描述符中指定的类的一个实例，并设置管理员提供的属性。这个类必须根据这些属性实现一个 <CODE>equals</CODE> 方法，这样应用服务器就可以保证它不会创建一个以上同样的实例。然后会调用 <CODE>start</CODE> 方法，向它传递一个实现了 <CODE>BootstrapContext</CODE> 接口的对象。可以用这个对象创建计时器、调度其他线程上的工作和控制导入的事务，在本系列的第 2 部分中将更详细地讨论所有这些内容。这个方法将不会堵塞并会及时返回。</P>
<P>应用服务器通常会在关闭或者解除部署资源适配器之前，对资源适配器调用 <CODE>stop</CODE> 方法。JCA 1.5 规范描述了这个过程的两个阶段。首先，应用服务器保证依赖资源适配器的所有应用程序都已停止。这可保证程序线程不再使用资源适配器对象，并且所有事务都已完成。然后应用服务器调用 <CODE>stop</CODE> 方法。这时，资源适配器将执行一个有序的关闭（例如，释放网络和应用服务器资源，并将所有缓存的数据强行送回后端）。调用 <CODE>stop</CODE> 后，应用服务器将不会重新使用资源适配器实例。</P>
<P>为了保留向后兼容性，<CODE>ManagedConnectionFactory</CODE> 没有改变，但是如果希望外部资源可以利用资源适配器具有的功能，那么还要实现清单 8 所示的新的 <CODE>ResourceAdapterAssociation</CODE> 接口。</P><A name=IDAF51JB><B>清单 8. <CODE>ResourceAdapterAssociation</CODE> 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>public interface ResourceAdapterAssociation {

    ResourceAdapter getResourceAdapter();
    void setResourceAdapter(ResourceAdapter ra)
            throws ResourceException;

}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>构建了托管连接厂之后，应用服务器将调用 <CODE>setResourceAdapter</CODE> 方法以便将它与其资源适配器关联在一起。在托管连接厂的生命周期内这种关联将会固定下来。这种方法只调用一次。</P>
<P><A name=IDAX51JB><SPAN class=atitle2>结束语</SPAN></A><BR>本文展示了 JCA 1.5 为现有出站契约带来的四项增强功能。惰性关联和征募优化会提高使用连接的应用程序的性能，验证托管连接厂会改善对故障情况的处理。在资源适配器级别引入生命周期管理为资源适配器提供了多种有趣的新机会。本系列的第 2 部分将分析如何在这个基础上建立新的工作管理和事务流入契约。</P><img src ="http://www.blogjava.net/mstar/aggbug/5387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-01 09:48 <a href="http://www.blogjava.net/mstar/archive/2005/06/01/5387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>轻量级开发的成功秘诀，第 1 部分: 核心原则及原理</title><link>http://www.blogjava.net/mstar/archive/2005/06/01/5386.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 01 Jun 2005 01:45:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/01/5386.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5386.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/01/5386.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5386.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5386.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;
<BLOCKQUOTE>轻量级开发是一个很大的主题，开发人员经常提到这个术语，但却很难讲明它的意思。本文是一系列讲述轻量级开发的文章中的首篇，介绍了该技术背后的核心原则及原理。</BLOCKQUOTE>
<P>1990 年，我发现了白水漂流并深深爱上了它。我们哪怕是经过最小的湍流，都会留一个人在湍流尾部，两个人在岸边用绳索拽着。我们认为这可以防止任何糟糕的事情发生。虽然看起来一切尽在掌握，只是有些不太实际。我们还学会了从船舱观察普通的湍流，并设法互相协作。对于大多数危险的湍流，我们花费了更多的时间来保障安全，但是只有少数情况下，这些措施才起到重大的作用。</P>
<P>在漂流过程中，使用一种起源于东南部湍急河流的轻量级策略，为我节省了时间，使我可以划得更远，玩得更开心，而无需过多考虑安全问题。在业务领域，轻量级开发让您可以按时完工，积极响应客户，从而节省时间和金钱。</P>
<P>在本系列的文章中，我关注于轻量级开发（曾经有太多含义的术语）的基础。本篇文章作为第一篇，为读者打好基础，同时对轻量级开发做出定义。后面的文章由浅入深地讲述从过程到原则最后到工具的知识。我也将在更高的级别上关注原理和架构的实现，并且提供具体实现的代码。</P>
<P>本系列面向没有经过太多轻量级开发的读者。如果您已使用了两年的 Spring 轻量级容器和敏捷过程，您可能会收获更多。如果您在传统的开发过程中使用 Enterprise JavaBeans™（EJB），但想要转向轻量级开发，那么本系列就是为您准备的。</P>
<P>我更多地是想在这场席卷整个 Java™ 技术社区的潮流中，做一些自己的贡献。<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">轻量级</I> 这个噱头为诸如 Spring 和 Pico 这样的容器增添了几分优雅。并且，一些源自轻量级过程的技术，如自动化单元测试，现在也渗透到了很多开发工作室中。</P>
<P><A name=IDATC2FB><SPAN class=atitle2>戳穿针对轻量级开发的谣言</SPAN></A><BR>“轻量级开发”通常与一套开发方法、框架和设计原理一起使用。</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>轻量级<I>方法</I> 包括敏捷过程，例如<I>极限编程</I>（XP）和 Scrum。它们强调开发中测试第一，积极调动客户和重构。</LI>
<LI>轻量级<I>框架</I> 鼓励人们使用简单原始的 Java 对象（POJO）编程，而不是类似 EJB 的重量级面向组件模型。</LI>
<LI>轻量级<I>设计模式</I> 使您可以在对象和集成服务之间进行松散耦合，而无需艰苦地编写业务逻辑或领域模型。</LI></UL>
<P>当我们研究这些思想和技术时，您将会学到更多关于它们的知识。但是首先让我们戳穿一些谣言。</P>
<P><A name=IDAKD2FB><SPAN class=atitle3>谣言：轻量级开发只是一种“玩具”技术。</SPAN></A><BR>许多开发工具，如 Microsoft® Visual Basic 或 PHP，它们通常不能驾驭或管理大型企业项目，因此得到了<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">“玩具”</I>的称号。像 Spring 和 Hebernate 这样的轻量级技术就常常因此而黯淡。实际上，大多数轻量级技术用于了企业开发，因为其他技术都使我们非常失望。Spring 框架就是作为代替 EJB 的一种轻量级技术。同样，XP 方法吸收改进了企业中的错误设置。我在获得 Jolt 大奖的 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Better, Faster, Lighter Java</I> 一书中，为大家讲述了有关成功部署在我的客户站点上的一些工具的信息，客户有一些是财富 500 强中的公司。轻量级技术在企业领域内，正在蓬勃发展着。</P>
<P><A name=IDAVD2FB><SPAN class=atitle3>谣言：轻量级开发策略构建的是“玩具”。</SPAN></A><BR>也许您更倾向于相信轻量级开发只对构建“玩具”应用程序有益。而您的目标是精确地满足客户的需求。让我们首先明确一下：轻量级技术完全可以构建这种规模的应用程序。实际上，这种巨大的反差经常发生，因为只有简单、简洁、无状态的设计才能使基础设施更好地工作。</P>
<P><A name=IDA1D2FB><SPAN class=atitle3>谣言：轻量级过程使您忽视规范。</SPAN></A><BR>在轻量级开发中，您需要认真地规划并与客户磋商需求。您必须构建严格的自动化单元测试，以优化重构。当放弃变更时，仍可以保持程序完整。而且，测试用例失败或变更引发错误时，自动化构建会通知您。轻量级开发必须比其他技术更加注意规范，但这种规范源于不同方面。</P>
<P>我认为这种开发风格超越了单一的技术或过程。如果您想轻松一些，那么需要选择使它易于工作的原则、过程和技术。</P>
<P><A name=IDACE2FB><SPAN class=atitle2>原则</SPAN></A><BR>该说的也说了，该做的也做了，您现在需要决定哪些需要重视，并据此制定决策。如果我觉得客户被误导或漠不关心，我通常会首先帮助他们建立核心原则。下面的列表是一个不错的起点： </P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI><B>争取简单性</B>。这种观念应该渗入到您所有的工作中。您的过程应该刚好生成足够完成工作的工件。开发人员应该尽量使用最简单的方法解决问题。您的工具应该使您构建一个清晰、简洁的解决方案。</LI>
<LI><B>修补漏洞</B>。许多开发方法可能不鼓励在过程中进行重构或变更，因为这些行为不直接用于产生客户代码。轻量级开发要求可以自由地修补太复杂或充斥 bug 的代码。您需要为它做出预算。</LI>
<LI><B>自动化单元测试</B>。您应该优先编写测试用例。您可能还没有在测试第一的开发中成功过，但测试会间接给您带来重构代码的便利。我以后会进一步讲述：广泛的单元测试改善您的客户体验，并提高代码的设计水平，这是因为它强迫您解耦联系过于紧密的代码。</LI>
<LI><B>使用短开发周期并积极调动客户参与其中</B>。许多顶级的软件工作室通过剔除不必要的工件来简化开发周期。如果您已经顺利得到客户的参与，那么很多的功能规范会变得越来越没必要。客户会很满意这种交互，并感激您的短周期开发，因为这稳步提高了客户的业务价值。</LI></UL>
<P>这些原则并不能完全包含您的技术抉择和开发过程，但它有利于您描述开发体验。如果经理也了解并遵循这些原则，开发人员就不至于做出无效技术选择，或者开发一些不必要的工件。确立原则后，就该规划一个有效过程了。</P>
<P><A name=IDAXE2FB><SPAN class=atitle2>过程</SPAN></A><BR>紧凑、快速的开发过程通常从<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">敏捷开发方法</I> 当中得到灵感。然而，这些方法并不针对每个人。如果您有一个大型团队，并且没有实际访问客户或合适的代理人，那么传统方法更适合您一些。但多数项目都有小团队 —— 不超过 12 个人，他们可以充分访问客户，以灵活使用这种方法。通常，敏捷开发包括下列原则：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>专注现场客户和代码，而不是其他设计技巧。您可以使用其他技巧，但只在它们对您确实有益的情况下。本过程不需要它。</LI>
<LI>简化您需要的文档。为了需要，宁可使用电子表格中的一行来描述，也不使用令人困惑的用例图。</LI>
<LI>只做足以完成工作的设计工作。不要对设计或性能过分忧心忡忡，使自己陷入绝境。</LI>
<LI>为了开发，努力进行简化并保证至少每天都集成您所构建的程序，必要时进行重构。</LI>
<LI>自动化测试。</LI></UL>
<P>即使您工作在传统的机构，您也可以利用已裁减的开发过程。技巧是推广<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">原则</I> 而不是<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">方法</I>。推广极限编程管理器 —— 或其他冠以<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">极限</I> 的东西，这可能会很艰难。但推广类似单元测试的原则通常更有意义。实际上，我的许多客户使用这种技术同敏捷开发过程一起为保守的机构服务，但他们的老板丝毫不知道有什么发生了改变。</P>
<P>用修辞手法描述一下这种技术。<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">原则</I> 是重拳出击的轻量级思想。<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">过程</I>是重量级的，实现起来将会很困难。</P>
<P><A name=IDAVF2FB><SPAN class=atitle2>技术</SPAN></A><BR>我已经概述了大多数轻量级开发人员需要了解的设计原理，以及利用这些原理的重要开源技术。</P>
<P><A name=IDA2F2FB><SPAN class=atitle3>依赖注入</SPAN></A><BR>最新一代容器称为<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">轻量级容器</I>，它们使用一个共同设计原理：<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">依赖注入</I>。 对这个简单思想来说，这是一个复杂的术语。依赖注入让您将对象和它所依赖的东西交给第三方。然后第三方创建所有对象并将它们绑在一起。比方说，称为 <CODE>myDao</CODE> 的数据访问对象需要一个称为 <CODE>ds</CODE> 的数据源。那么该容器会一同创建它们，并设置一个属性： </P><A name=IDASG2FB><B>清单 1. 创建一个第三方汇编程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
myDao = new Dao();
ds = new DataSource();
myDao.setDataSource(ds);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>当然，不创建这种第三方汇编程序的话，您也可以使用框架来做其他附加的工作（如提供配置支持）。Spring 框架、Pico 和 HiveMind 就扮演了这个角色。其他像 JavaServer Faces(JSF) 框架也利用了依赖注入。</P>
<P><A name=IDAZG2FB><SPAN class=atitle3>面向对象编程</SPAN></A><BR>使用面向对象编程（AOP），您可以编写通用的功能性模块（称为<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">方面</I>） —— 例如，日志、事务、安全或持久性。AOP 使您可以将这些方面联系到 POJO，然后指定一个时间点（如方法开始时或产生异常时）和另一个需要联系的方面。例如，您可能想要创建一个外观事务对象。您可以在调用方法时将 <CODE>TransactionBegin</CODE> 方面关联到外观方法。然后在产生异常时将 <CODE>RollBack</CODE> 方面关联到外观的异常。最后，在方法结束时将 <CODE>Commit</CODE> 方面关联到外观的方法。您在配置中完成这些工作，而不是通过编写代码。依靠这种能力，您可以创建一个简单的 POJO 事务、安全或远程访问。</P>
<P>您现在已经得到了关于 POJO 的声明性事务，这对企业应用程序非常有用。使用这些工具，您可以完全放弃 EJB，或者最小化它的作用。而这正是轻量级组件所要做的。</P>
<P><A name=IDAPH2FB><SPAN class=atitle3>透明持久性</SPAN></A><BR>持久性也是建立在较简单的编程模型之上。透明持久性框架通过配置而不是编写代码，来使您为应用程序添加持久性。因为大多数应用程序是面向对象的，并且访问一个关系数据库，所以一些专家断言，我们最终将进入对象关系映射的时代。我目前发现的顶级持久性解决方案是 SolarMetric 的 Kodo JDO 和 Hibernate（参阅 <A href="http://www-128.ibm.com/developerworks/cn/opensource/os-lightweight1/index.html?ca=dwcn-newsletter-opensource#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。在后面的文章中我将详细比较这些解决方案。其他轻量级解决方案，例如 iBATIS 和 Active Record 设计模式，根本不会试图进行对象关系映射。</P>
<P><A name=IDA0H2FB><SPAN class=atitle2>结束语</SPAN></A><BR>在轻量级开发中，您基本上可以：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>合并过程、技术和原理。</LI>
<LI>优先选择较简单的技术。</LI>
<LI>在一个稳固、轻量级的基础上，进行构建。</LI>
<LI>尽量争取最可能的透明性。</LI>
<LI>使用您可以利用的技术，如依赖注入和 AOP。</LI></UL>
<P>一定要明白，无论技术还是过程都不能完整定义轻量级开发。它是一个包罗万象的概念。伴随本系列的文章，您将看到对轻量级技术和原理的各种各样的讨论。我将首先关注开源框架，并着重讲述轻量级容器。后面的文章，我会讨论保守公司内的轻量级方法实现，甚至还有一些超越了 Java 技术的替代方案。我非常喜欢这个系列的文章，希望您也一样。</P><img src ="http://www.blogjava.net/mstar/aggbug/5386.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-01 09:45 <a href="http://www.blogjava.net/mstar/archive/2005/06/01/5386.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构建更佳的 J2EE 服务器，开源之路</title><link>http://www.blogjava.net/mstar/archive/2005/06/01/5384.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Wed, 01 Jun 2005 01:18:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/06/01/5384.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5384.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/06/01/5384.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5384.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5384.html</trackback:ping><description><![CDATA[<!--StartFragment -->&nbsp;<SPAN class=atitle2>Gluecode Software 的主要创始人 Jeremy Boynes，展望 Apache Geronimo 的远大前程</SPAN>&nbsp;<BR><!--StartFragment --> 
<BLOCKQUOTE><ABSTRACT-EXTENDED>Gluecode Software 是日益增多的成功地商业化开源软件的公司之一，它已经将好些很有前途的开源中间件组件（包括 Apache Geronimo 和 Apache Derby 等）合并到 J2EE 应用服务器堆栈中。在最近 IBM 宣布收购 Gluecode 公司后，我们与 Jeremy Boynes（Geronimo 的主要创建者和 Gluecode 的 CTO）进行了座谈，聆听了他对 Geronimo、Java™ 未来发展方向和开源状况的展望。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P><IMG height=187 alt="Jeremy Boynes" src="http://www-128.ibm.com/developerworks/cn/opensource/os-boynes/p-jboynes-cap.jpg" width=150 align=left border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" valign="top"> 当首席技术官 Jeremy Boynes 加入 Gluecode Software 时，他带来了关于将开源软件和企业级应用开发相结合的第一手资料。他过去是 Bravanta 和 Netmosphere 的首席架构师，通过使用开源软件，他帮助这两家公司降低了成本开支。他拥有 20 年的企业计算经验，曾在 Cisco、BT、Centrum Systems 和 Sequent Computer Systems 任职。他拥有电子工程师学位，并且作为创建者和负责人参与了众多的大型开源项目，包括 OpenEJB、ObjectWeb、Derby Java 数据库以及 Apache 基金会的 Geronimo J2EE 服务器项目。 </P>
<P>IBM 已经宣布收购 Gluecode Software，这符合它扶持和参与开源社区，同时鼓励采用开放标准的目标。像 Eclipse、Derby 和 Apache HTTP 服务器（httpd）一样，IBM 引进 Gluecode Software 的基于 Geronimo 的平台，来为它现有的中间件阵容提供开源中间件。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width=250 align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不要认为 Geronimo 仅仅只是另外一个 J2EE 服务器，其实它是用来构建各种各样特定基础设施服务的系统框架的一个开端。 </B><BR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">&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;—— Jeremy Boynes</P></TD></TR></TBODY></TABLE></P>
<P>我们的 developerWorks 成员问 Jeremy：作为 Geronimo 的架构师之一，请为我们介绍一下 Geronimo 的设计目标、架构以及它是如何脱颖而出的。或许您还可以给我们一些关于这种规模的开源项目是如何聚合在一起的方面的见解。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>您可以描述一下 Geronimo 的架构么？它的主要组件是什么？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Jeremy Boynes：</B>从 Geronimo 一开始，项目的主要目标就是，通过将支持 J2EE 规范不同部分的许多现有开源项目进行集成，以产生 J2EE 1.4 的实现。该架构专注于两个主要方面：提供一个框架，该框架有助于集成，但是对其他项目毫无影响；提供一组系统服务模块，这些模块组装在一起就成为最终的服务器。 </P>
<P>该架构的核心是 Geronimo 内核和 GBean 框架。该核心提供一个基础设施，用于控制其他服务是如何配置、激活和管理的，并且控制它们之间的依赖性是如何分辨的。这个核心已经保持得很小，这使得 Geronimo 可以压缩到最小的设备中。 </P>
<P>实际上该核心有两个变种。一个是轻量级的，设计用于命令行工具或非托管服务器。另一个设计用于传统的服务器，并且使用 JMX 以便所有组件都得到管理和监控。 </P>
<P>在该核心中，我们安装了一系列模块，它们提供诸如事务、安全、日志记录、命名、远程控制之类的系统服务 —— 应用程序期望使用的基础设施是可用的。 </P>
<P>通过使用这种灵活性，我们可以组装这些服务以形成特定的应用程序环境。例如，该项目的主要目标是产生一个 J2EE 1.4 环境，我们通过将合适的服务组装在一起实现了这个目标。尽管这种组装模型也可以用于其他配，但是我们主要为 Spring 社区工作以产生一个直接支持 Spring Framework 的组装模型。 </P>
<P>这种灵活性功能极其强大，但它在配置控制和管理方面也有其自身的困难。为了解决这些困难，我们在内核中构建了一个强大的配置管理系统，这允许模块被组装、注册和封装，以便安装到任何 Geronimo 内核中。特定服务器具体运行哪些配置可由该服务器本身进行控制，如果在企业级环境中，则由外部管理服务进行控制。 </P>
<P>我们实际上也将这种配置管理功能扩展到终端用户应用程序。这使得机构可以在集成或测试环境中创建一个已完整部署的应用程序，然后将其确切的二进制版本经过发布过程转移到生产环境，或者分发给客户。 </P>
<P>额外的好处是，通过离线完成所有部署，我们可以实际减少生产服务器中的开销。它也允许我们在部署期间执行广泛的优化，即潜在地扫描字节码和优化代码路径，而不会影响在线系统。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>我知道该项目的关键需求之一就是要完全遵循 J2EE 标准。为什么要这么做呢？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>这么做基本上是因为，它对我们的用户非常重要。J2EE 在为应用程序定义框架这方面做了很多贡献。这些应用程序由不同机构独立实现，但它们的行为在所有平台上都是一致的（经 Compatibility Test Suites 验证过）。这为企业用户提供了一种保证，如果他们选择 J2EE，那么不会只针对单独的供应商解决方案编写应用程序，或者说应用程序供应商可以一次编写应用程序，然后在任何客户环境中运行。 </P>
<P>当然，现实世界中的 J2EE 规范还是不完整的，直至今日，仍有一些领域需要知道正在使用的是哪种实现。然而关键是，用户开始选择他们针对特定解决方案的局限程度。当然，使用开源这种限制会少很多。 </P>
<P>证明还只是第一步。为了成功，Geronimo 需要在基本规范之上，为用户提供明显的优势。在该项目中，我们优先关注企业环境中对生产部署影响最大的技术领域。它们主要是与管理、配置、可靠性、可伸缩性和性能相关的特性。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>J2EE 规范的哪些部分很难处理，哪些部分比较容易处理？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>项目过程中曾经流传着一个关于“88-itis”笑话 —— 为实现 JSR-88 部署规范的每个工作人员开始都用各自的方言交流，然后很快就变得混乱起来。 </P>
<P>需要着重说明的是，该规范的某些部分处理起来非常容易，这是因为我们可以集成许多优秀的开源解决方案。例如，对于 Web 容器我们可以集成 Jetty 服务器或 Apache Tomcat，两者都具有优秀的“血统”。同样，我们的 JAXP（用于 XML 处理的 Java API）实现使用了 Apache Xerces，我们的 JMX（Java 管理扩展）实现使用了 MX4J（JMX 的开源实现），我们还基于 Apache Derby（以前称为 Cloudscape）提供了一个嵌入式数据库，等等。 </P>
<P>面临严峻挑战的领域，同时也就是阻挠所有 J2EE 实现的领域：使用 Web 服务和 IIOP 与其他服务器的互操作。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>已经存在许多能够胜任的商业 J2EE 服务器。为什么 Geronimo 开源还如此重要？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>也有其他的开源 J2EE 服务器，例如 ObjectWeb 的 JOnAS。像 Geronimo 这种项目只能通过开源才能成功，此外，为了获得商业实体和独立开发人员的支持，它也必须在类似 Apache License 的 BSD 许可证下进行开发，因为该许可证允许开发混合开源项目和专有技术的解决方案。 </P>
<P>例如，Geronimo J2EE 服务器通过 Apache Software Foundation 发布，它基于诸如 Apache Tomcat、Jetty、OpenEJB 和 ActiveMQ 等其他开源项目的组合。 </P>
<P>其他组合由该项目或其他机构提供。例如，系统供应商可能使用专有软件（直接依附于操作系统级别的日志）来替代事务管理程序，而企业用户可能使用集成他们自己的一致性和审计基础设施的安全策略来替代安全策略提供程序。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B> 您是否觉得 Geronimo 的组件化特性有助于开源的开发模型？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>我认为类似 Geronimo 采用的模块化方法，对于任何这种规模的软件项目都是很重要的，无论对于开源项目还是对于单独机构的内部项目。 </P>
<P>然而我们的最大获益是，开发受到开放社区的驱动。在社区中，无论个人还是公司都能够参与到项目中并从中获益。基于 Apache Software Foundation 及其强调开放、精英和协作的社区，能确保项目的长期发展，同时也防止项目受控于个人行为或单个机构的商业动机。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>作为完整的 J2EE 服务器，Geronimo 毫无疑问适合于大型的、分布式的、事务的企业级应用。但对于小型应用来说，哪些方面不适合于使用 Geronimo？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B> 组件化的架构使 Geronimo 可以从小内存设备一直扩展到企业级应用。我们特别注意将内核保持为占用较小的内存，这样它就可以用于受限的设备，如机顶盒。然后，用户可以根据将要运行的应用程序的需要，选择哪些服务需要配置到框架中。 </P>
<P>例如，用于分支机构的简单服务器可以配置一个 Web 容器、安全代理，还可以配置一个消息发传递客户机，然后在本地运行应用程序。远程管理和配置功能方便了从一个中心位置对大量设备的管理。 </P>
<P>因为 Geronimo 实际上用于服务器应用程序，因此现在可能还并不适合考虑部署到手持或蜂窝设备。尽管如此，随着这些设备功能的不断增强，未来我们也许会探索这一领域。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>您能谈一谈将可伸缩性构建到 Geronimo 中的一些方法么？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>很高兴您提到的是可伸缩性而不是单纯的性能（尽管我们在这个领域也非常出色）。在这个使用普通服务器硬件和零许可证成本软件的年代，可伸缩性常常使我们能够更有效地将应用程序扩展到更多的机器，而不是像以前升级为更庞大的服务器。 </P>
<P>预配置应用程序和根据需要定制服务器配置的能力，使用户可以将服务器资源更多地投入到应用程序的工作当中，而不是应用服务器的管理开销。 </P>
<P>可伸缩性的传统制约因素是对 CPU、内存和 I/O的访问。为了管理 CPU 资源，我们提供了一套线程池。通过调节线程池，可以均衡对入站 Web、EJB 和连接器请求或者由服务器本身生成的请求（比如事务回滚）可用的资源。未来，我们打算将不同服务的线程池组合为一个单独的工作管理基础设施，用于均衡负载。我们也引入了中央事务上下文这一核心概念，它使组件无需在共享线程池或缓存上同步，就可以访问每一个事务信息。 </P>
<P>为了满足内存的可伸缩性，我们认真地控制执行任务时容器分配的内存总量。不幸的是，我们控制不了应用程序自身需要的资源。随着我们从实际应用程序中获得经验，这是一个仍需调整的领域。 </P>
<P>我们在适当的地方使用了 NIO（Java 的最新 I/O）功能，以提高 I/O 的可伸缩性。我们特别关注的领域是事务日志，在该领域中，通过与 ObjectWeb 的协作建立了 HOWL 项目 —— 非常高效的日志子系统。 </P>
<P>最后，这个问题还处于早期阶段。随着 Geronimo 在真实场景中的压力测试，实际的信息将会公诸于众。这方面的问题也一定会引起开发人员的关注。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>项目的最大挑战和最大成功是什么？ </P>
<P>最大的挑战就是项目本身所具有的复杂性，需要实现整个平台。这些都是在协作但独立的开源项目生态系统上进行的，这平添了几分兴奋。我想这也是最大的成功 —— 这么多的人能够团结在一起，共同完成它。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>项目开始时，Geronimo 已经构建到什么程度了，还有多少需要编写？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>项目开始时，并没有实际构建 Geronimo 内核。但是许多可以集成的项目 —— 比如 OpenEJB、MX4J、Jetty —— 都已经有了，并且早已存在了。这些项目的绝大多数都需要进行改进以符合 J2EE 1.4 规范。而当我们 2003 年 8 月启动项目时，该规范还没有最终定案。 </P>
<P>我们现在马上就要完成了，但是即使经过了完全验证，在性能调优、可用性、国际化、文档化、新特性等到方面仍需努力。 </P>
<P>坦白地说，随着项目的发展，我们知道许多领域需要改进，我们也期望把更多的新思想融合到其中。还有很多工作要做，社区是向每个人开放的，欢迎大家积极参与。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>除了日常开发的项目之外，还有其他喜欢的项目吗？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>Apache Derby。看到它走向开源，我非常兴奋。实际上，我以前使用过 Cloudscape。看到这样的项目加入 Apache 并可用，我非常兴奋，这使我能够更加随心所欲地开发自己的数据库。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>您认为开源在企业中已经有一席之地了么？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>企业与以前相比，更加接受开源解决方案，尤其是最近的 12 到 18 个月。企业基础设施之一 —— 操作系统对 Linux 的广泛采用，向我们展示了企业越来越易于接受开源技术。我们看到，随着企业开始对用于数据库和应用基础设施的开源技术进行评估，开源技术得到了不断提升。这些成熟的市场具有清晰定义的规范（适用于商业化），我期望 3 年到 5 年后，开源解决方案在这些市场中会扮演一个重要的角色。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>如果需要修改开源的某些部分，您认为会是哪里呢？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>有许多东西需要修改。最重要的是，每个开源社区需要修改的都有所<I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">不同</I>。实际上根本没有开源运动，只有各种各样不同的社区和正在进行的不同形式的开发，开发涉及从 Linux 操作系统、Apache 项目（如 httpd 和 Geronimo），到诸如 Eclipse 和 ObjectWeb 之类的联盟，或者一些 SourceForge 项目中人们喜爱的桌面应用程序。有太多各种各样的问题，所以我很难说出具体哪些部分需要改进。 </P>
<P>开源的一部分挑战仍是通信、协作、设法合作、理解商业公司扮演的角色，但仍要考虑个人所做的贡献，等等。所以最大的挑战是文化。每个单独的项目都通过不同的方法应对这些挑战。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>Java 的下一件大事是什么？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>是 Geronimo [笑]。Java 语言正处于一个令人关注的阶段，越来越多的企业应用开始采用它。我们正在进入一个崭新的阶段，即通过在应用基础设施级别上使用托管运行时环境和虚拟化技术，可以大大方便人们的工作。所以，我想我们不久将会看到一个令人兴奋的增长期。 </P>
<P>我们已经看到这种独立趋势的蔓延，像 Spring Framework 就非常符合用户所想。我认为这种百花齐放的情形非常好。而且，也有标准化的过程，因此企业可以完全放心这种技术的未来发展。我们处在一个令人瞩目的时代，太多的改革正在进行，太多的新思想不断涌现，太多的新事物正经受考验，例如 AOP（面向方面编程）和其他一些轻量级框架。我想这一切都非常令人鼓舞。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks：</B>您还有其他想要与我们 developerWorks 读者一起分享的想法么？ </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JB：</B>从技术观点来说，惟一最重要的是，我希望我们不要再将 Geronimo 只是作为另一个 J2EE 服务器，而是要把它作为构建各种各样基础设施服务的系统框架的一个开端。按照这种想法，制约该框架的只会是我们的想像力。快来参加我们，帮助我们一起构建未来。 </P>
<P>同时，我要感谢所有相关人员，因为太多我在这里无法一一提到。感谢为 Geronimo 项目或其他相关项目做出贡献的人们！ </P><img src ="http://www.blogjava.net/mstar/aggbug/5384.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-06-01 09:18 <a href="http://www.blogjava.net/mstar/archive/2005/06/01/5384.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hoho，今天发现了XML-RPC！</title><link>http://www.blogjava.net/mstar/archive/2005/05/31/5378.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 31 May 2005 15:53:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/05/31/5378.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/5378.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/05/31/5378.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/5378.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/5378.html</trackback:ping><description><![CDATA[今天在TSS上发现了XML-RPC的帖子，就粗略的看了些资料，感觉这种轻量级的跨平台远程调用很有意思啊。用起来简单，据说速度也很快（自己没有试过<IMG height=20 src="http://www.blogjava.net/Emoticons/hitwall.gif" width=25 border=0>）。<BR><BR>下面贴一些资料：<BR><BR><!--StartFragment -->&nbsp;XML-RPC 是工作在 Internet 上的远程过程调用协议。通俗点讲，就是使用 HTTP 协议交互，交互的载体是 XML 文件。XML-RPC 具体的规范说 明请参考<A href="http://www.xmlrpc.com/spec"><FONT color=#ff0000>这里</FONT></A>。 
<P align=center><IMG height=270 alt="" src="http://www.sentom.net/img/xmlrpc.jpg" width=527 border=0> <BR>图片来自XML-RPC官方网站 </P>
<P>XML-RPC 规范定义了六种数据类型，下表是这六种数据类型与 Java 的数据类型对应表。 </P>
<P>
<TABLE cellSpacing=1 cellPadding=1 align=center bgColor=#164781>
<TBODY>
<TR bgColor=#c2d1e7>
<TD>XML-RPC</TD>
<TD>Java</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;i4&gt; 或 &lt;int&gt;</TD>
<TD>int</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;boolean&gt;</TD>
<TD>boolean</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;string&gt;</TD>
<TD>java.lang.String</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;double&gt;</TD>
<TD>double</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;dateTime.iso8601&gt;</TD>
<TD>java.util.Date</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;struct&gt;</TD>
<TD>java.util.Hashtable</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;array&gt;</TD>
<TD>java.util.Vector</TD></TR>
<TR bgColor=#ffffff>
<TD>&lt;base64&gt;</TD>
<TD>byte[ ]</TD></TR></TBODY></TABLE></P>
<P>XML-RPC 规范的各种平台都有具体实现，XML-RPC 规范的 Java 实现都有好几种，这里我们选择了 <A href="http://ws.apache.org/xmlrpc/index.html"><FONT color=#ff0000>Apache XML-RPC</FONT></A>。 </P>
<H5>XML-RPC 服务端实现</H5>先定义一个简单业务对象 MyHandler，远程客户端将调用该对象的方法，具体代码如下： 
<P></P><PRE class=programlisting>package net.sentom.xmlrpc;

public class MyHandler {
	
	public String sayHello(String str){
		return "Hello," + str;
	}
}
</PRE>
<P>然后定义一个 Servlet 名叫 MyXmlRpcServer，远程客户端通过 HTTP-POST 访问该 Servlet。 </P>
<P></P><PRE class=programlisting>package net.sentom.xmlrpc;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.xmlrpc.XmlRpcServer;

public class MyXmlRpcServer extends HttpServlet {
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		XmlRpcServer xmlrpc = new XmlRpcServer();
		xmlrpc.addHandler("myHandler", new MyHandler());
		byte[] result = xmlrpc.execute(request.getInputStream());
		response.setContentType("text/xml");
		response.setContentLength(result.length);
		OutputStream out = response.getOutputStream();
		out.write(result);
		out.flush();
	}
}
</PRE>
<P>需要特别说明是： </P>
<P></P><PRE class=programlisting>xmlrpc.addHandler("myHandler", new MyHandler());
</PRE>
<P>为了便于理解，这里可以看成普通的： </P>
<P></P><PRE class=programlisting>MyHandler myHandler = new MyHandler();
</PRE>
<P>最后在web.xml文件中加入以下几行： </P>
<P></P><PRE class=programlisting>&lt;servlet&gt;
    	&lt;servlet-name&gt;MyXmlRpcServer&lt;/servlet-name&gt;
    	&lt;servlet-class&gt;net.sentom.xmlrpc.MyXmlRpcServer&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
    	&lt;servlet-name&gt;MyXmlRpcServer&lt;/servlet-name&gt;
    	&lt;url-pattern&gt;/MyXmlRpcServer&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
</PRE>
<H5>XML-RPC 客户端实现</H5>客户端相对简单一些，先来一个 Java 客户端实现 MyXmlRpcClient: 
<P></P><PRE class=programlisting>package net.sentom.xmlrpc;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Vector;
import org.apache.xmlrpc.XmlRpcClient;
import org.apache.xmlrpc.XmlRpcException;

public class MyXmlRpcClient {
	public static void main(String[] args) {
		try {
			XmlRpcClient xmlrpc = new XmlRpcClient("http://localhost:8080/XMLRPC/MyXmlRpcServer");
			Vector params = new Vector();
			params.addElement("Tom");
			String result = (String) xmlrpc.execute("myHandler.sayHello",params);
			System.out.println(result);
		} catch (MalformedURLException e) {
			System.out.println(e.toString());
		} catch (XmlRpcException e) {
			System.out.println(e.toString());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
</PRE>
<P>http://localhost:8080/XMLRPC/MyXmlRpcServer 为 MyXmlRpcServer 的访问URL。 </P>
<P></P><PRE class=programlisting>String result = (String) xmlrpc.execute("myHandler.sayHello",params);
</PRE>
<P>再来一个 Python 客户端实现 </P>
<P></P><PRE class=programlisting>import xmlrpclib
url = 'http://localhost:8080/XMLRPC/MyXmlRpcServer';
server = xmlrpclib.Server(url);
print server.myHandler.sayHello('Tom');
<!--StartFragment -->来源：<A href="http://www.sentom.net">http://www.sentom.net</A> <BR><BR><!--StartFragment --> <BR><P><!--StartFragment --> <B><FONT class=f22 color=#0e3e92><B>[原创]xml-rpc入门例程及一个通用服务器</B></FONT></B> <BR>一，准备过程</P><P>远程过程调用RPC，基于XML的传输方式，当然低层API，就不用我们操心了，但必须的软件还是要有的，先给个列表清单<BR>JDK1.4.2 不用说了<BR>Xerces解析器&nbsp; 到<A href="http://xml.apache.org/">http://xml.apache.org/</A>上去下载吧，<BR>XML-RPC开发包, <A href="http://ws.apache.org/xmlrpc/">http://ws.apache.org/xmlrpc/</A>上可以下得到</P><P>将以上所有的jar包放到开发环境的classpath中。</P><P>二，Hello World </P><P>XML-RPC如果想跑起来，最后需要四个组件，WEB server, 服务器类，处理类，客户类</P><P>1.WEB Server.</P><P>在我们已经下载的XML-RPC包中就有一个轻型级的WEB SERVER。<BR>在程序中，我们只需要简单的用以下语句就可以启动。<BR>//建立一个对象，传输一个端口<BR>WebServer server = new WebServer(Integer.parseInt("8989"));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>//启动<BR>server.start(); </P><P>2.编写处理类</P><P>处理类相当RMI中的远程类，只不过在这里更轻量类，无须任何接口.</P><P>在这个类中包含一个或多个公有方法以供远程的客户端来调用。</P><P>public class HelloHandler {</P><P>public String sayHello(String name) {<BR>return "Hello " + name;<BR>}</P><P><BR>}</P><P><BR>3.服务器</P><P>负责调用以上代码来启动用服务器，同时还要将远程对象绑定到该服务器上。</P><P>import java.io.IOException;<BR>//引入必须的包，当然你的xml-rpc的包应该在classpath中<BR>import org.apache.xmlrpc.WebServer;<BR>import org.apache.xmlrpc.XmlRpc;<BR>public class HelloServer {<BR>&nbsp;/**<BR>主方法<BR>&nbsp;&nbsp;&nbsp;&nbsp; */<BR>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 使用Xerces的XML解析器<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 给出提示，并在端8989上启动服务器<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Starting XML-RPC Server...");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WebServer server = new WebServer(Integer.parseInt("8989"));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.start();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 将HelloHandler类的实例编定到WEB SERVER上，hello是该处理类的标识，在客户端调用时要用得到<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.addHandler("hello", new HelloHandler());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Registered HelloHandler class to \"hello\"");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (ClassNotFoundException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Could not locate SAX Driver");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Could not start server: " + <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.getMessage());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; }<BR>}</P><P>4.客户端<BR>根据“标识名.方法名“来定位远程的处理方法。</P><P>import java.io.IOException;<BR>import java.net.MalformedURLException;<BR>import java.util.Vector;</P><P>//导入必须的包<BR>import org.apache.xmlrpc.XmlRpc;<BR>import org.apache.xmlrpc.XmlRpcClient;<BR>import org.apache.xmlrpc.XmlRpcException;</P><P><BR>public class HelloClient {<BR>&nbsp; </P><P>&nbsp;&nbsp;&nbsp; public static void main(String args[]) {<BR>&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String yourname="liu xiaobai";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 使用 Apache Xerces SAX 解析器<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 定位远程服务器，http://主机地址:端口号， 8989是上文服务器启动时用的端口<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlRpcClient client = <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new XmlRpcClient("<A href="http://localhost:8989/">http://localhost:8989/</A>");&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 创建调用请求，方法的参数列表如果一个Vector对象来存储。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Vector params = new Vector();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params.addElement(yourname);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 发出请求，并返回结果,execute需要两个参数，第一个参数用“标识名.方法名”,第二参数是一个 刚刚建立的向量对象</P><P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String result = <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (String)client.execute("hello.sayHello", params);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Response from server: " + result); </P><P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (ClassNotFoundException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Could not locate SAX Driver");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MalformedURLException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Incorrect URL for XML-RPC server format: " + <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.getMessage());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (XmlRpcException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("XML-RPC Exception: " + e.getMessage());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (IOException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("IO Exception: " + e.getMessage());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; } <BR>}</P><P>5,编译以上代码，要确保解析器，XMP-RPC开发包的jar文件在classpath中。</P><P>运行服务器</P><P>&nbsp;&nbsp; java&nbsp; HelloServer</P><P>运行客户端</P><P>&nbsp; java HelloClient</P><P>&nbsp;</P><P>6.一个通用的XML服务器</P><P>功能描述：通过配置文件来配置要加载的处理器</P><P>1.配置文件的名称及位置</P><P>名字：config.properties</P><P>类文件的根目录中，如果你类是默认包，那么就是同一个目录；如果类的包名是com.hello,那么该文件应该与com目录放在同一级中。</P><P>内容：</P><P>#标识名=类名<BR>hello=javaxml2.HelloHandler</P><P><BR>2.通用的源代码</P><P>import java.io.*;<BR>import org.apache.xmlrpc.*;<BR>import java.util.Properties;<BR>import java.util.Enumeration;<BR>import java.util.Hashtable;<BR>public class MyLightXMLServer<BR>{<BR>&nbsp;&nbsp; private WebServer server;<BR>&nbsp;&nbsp; private int port;<BR>&nbsp;&nbsp; private String configfile;<BR>&nbsp;&nbsp; <BR>&nbsp;&nbsp; public MyLightXMLServer(int port,String config)<BR>&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp; this.port=port;<BR>&nbsp;&nbsp;&nbsp; this.configfile=config;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; //启动服务器<BR>&nbsp;&nbsp;&nbsp; public void start() throws IOException,ClassNotFoundException,Exception<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp; XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");<BR>&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("starting up xml-rpc server...");<BR>&nbsp;&nbsp;&nbsp;&nbsp; server=new WebServer(port);<BR>&nbsp;&nbsp;&nbsp;&nbsp; //调用注册函数<BR>&nbsp;&nbsp;&nbsp;&nbsp; registerHandlers(this.getHandlers());<BR>&nbsp;&nbsp;&nbsp;&nbsp; server.start();<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; public void registerHandlers(Properties handlers) throws Exception<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp; Enumeration enum=handlers.propertyNames();<BR>&nbsp;&nbsp;&nbsp;&nbsp; while(enum.hasMoreElements())<BR>&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String temp=(String)enum.nextElement();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String tempcls=(String)handlers.get(temp);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls=Class.forName(tempcls);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.addHandler(temp,cls.newInstance());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; public Properties getHandlers()<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp; try<BR>&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Properties properties=new Properties();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; properties.load(new FileInputStream(new File("config.properties")));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return properties;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<BR>&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; return null;<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; public static void main(String args[])<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp; String port="8989";<BR>&nbsp;&nbsp;&nbsp;&nbsp; String configfile="";<BR>&nbsp;&nbsp;&nbsp;&nbsp; MyLightXMLServer server=new MyLightXMLServer(Integer.parseInt(port),configfile);<BR>&nbsp;&nbsp;&nbsp;&nbsp; try<BR>&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.start();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<BR>&nbsp;&nbsp;&nbsp;&nbsp; {e.printStackTrace();}<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;}</P><P>将MyLightXMLServer .java编译并运行之后，该服务器会在配置文件中加载该处理器</P><P><BR>然后可以直接执行HelloClient</P><P>java HelloClient<BR><BR><BR><BR><BR></P></PRE><img src ="http://www.blogjava.net/mstar/aggbug/5378.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-05-31 23:53 <a href="http://www.blogjava.net/mstar/archive/2005/05/31/5378.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>6 Best Practices for J2EE Architecture</title><link>http://www.blogjava.net/mstar/archive/2005/05/19/4877.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 19 May 2005 10:34:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/05/19/4877.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/4877.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/05/19/4877.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/4877.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/4877.html</trackback:ping><description><![CDATA[<div class="Section1" style="">

<p class="featurehead"><span lang="EN-US">6 Best Practices for J2EE Architecture <br>
<span class="featuredek">Leverage "in-the-trench" J2EE best practices
to improve the architecture and design of your existing and future J2EE
applications.</span><br>
<span class="GramE"><span class="aboutauthor">by</span></span><span class="aboutauthor"> <span class="SpellE">Tarak</span> <span class="SpellE">Modi</span></span>
<o:p></o:p></span></p>

<p><em><span style="font-family: 宋体;" lang="EN-US">Posted
June 28, 2004</span></em><span lang="EN-US"><o:p></o:p></span></p>

<p><span class="dropcap"><span lang="EN-US">N</span></span><span lang="EN-US">umerous
articles have discussed J2EE best practices. So, why am I writing another one,
and how is this article any different from—or better than—the others? <o:p></o:p></span></p>

<p><span lang="EN-US">First, this article is aimed at practicing technical
architects. To avoid insulting anyone's intelligence, I'll avoid the cliché
best practices such as "build daily," "test everything,"
and "integrate often." Any projects with architects worth their salt
would have well-defined team structures with properly delineated roles. They
would also have properly documented processes for conducting code reviews,
building the code (daily and on-demand), testing (unit, integration, and
system), deployment, and configuration/release management. <o:p></o:p></span></p>

<p><span lang="EN-US">Second, I'll skip commonly touted best practices such as
"interface-based design," "use well-known design patterns,"
and "use service-oriented architecture." Instead, I'll focus on six
(out of many) "in-the-trench" lessons I have learned and followed
over the years. Finally, this article's intent is to get you thinking about
your architecture; providing working code samples or solutions is beyond this
article's scope. Without further ado, let's examine the six lessons.<o:p></o:p></span></p>

<p><span class="subhead"><b style=""><span lang="EN-US">Lesson
1: Never Shortcut Server-Side Validation</span></b></span><b style=""><span lang="EN-US"><br>
</span></b><span lang="EN-US">As a software consultant, I've had the opportunity
not only to design and implement Web applications, but to assess/audit many Web
applications as well. I often encounter Web pages within the application that
are sophisticated and packed with client-side JavaScript that performs
extensive checks on user-entered data. Even the HTML elements have data validation
attributes such as MAXLENGTH. The HTML form is submitted only upon successful
validation of all the entered data. As a result, the server side happily
performs the business logic once it receives the posted form (request). <o:p></o:p></span></p>

<p><span lang="EN-US">Do you see the problem here? The developers have made
several major assumptions. For example, they assume all Web application users
will be equally honest. The developers also assume all users will always access
the Web application through the browser(s) they've tested. And the list goes
on. These developers have forgotten that it's easy to simulate browser-like
behavior through the command line using freely available tools. In fact, you
can send almost any "posted" form by typing in the appropriate URL in
the browser window; although, you can easily prevent such "form
posting" by disabling GET requests for these pages. But you can't prevent
people from simulating or even creating their own browsers to hack into your
system.<o:p></o:p></span></p>

<p><span lang="EN-US">The underlying problem is that the developers have failed
to recognize the main difference between client-side validation and server-side
validation. The main difference between the two is <i>not</i> where the
validation occurs, such as on the client or on the server. The main difference
is in the purpose behind the validation. </span></p>

<p><span lang="EN-US">Client-side validation is merely a convenience. It is
performed to provide the user with quick feedback—to make the application
appear responsive and give the illusion of a desktop application. <o:p></o:p></span></p>

<p><span lang="EN-US">Server-side validation, on the other hand, is a must for
building secure Web applications. It ensures that all data the client sends to
the server is valid, no matter how the data was entered on the client side. <o:p></o:p></span></p>

<p><i><span lang="EN-US">Thus, only server-side validation provides real
application-level security</span></i><span lang="EN-US">. Many developers fall
into the trap of a false sense of security by performing all data validation
only on the client side. Here's a common example that illustrates the point: <o:p></o:p></span></p>

<p><span lang="EN-US">A typical logon page has a textbox to enter a username and
another textbox to enter a password. On the server side, one might encounter
some code in the receiving <span class="SpellE">servlet</span> that constructs a
SQL query of the form <code>"SELECT * FROM <span class="SpellE">SecurityTable</span>
WHERE username = '" + <span class="SpellE"><span class="GramE">form.getParameter</span></span><span class="GramE">(</span>"username") + "' AND password = '" + <span class="SpellE">form.getParameter</span>("password") + "';"</code>
and executes it. If the query comes back with a row in the result set, the user
is successfully logged in. If not, the user is not logged in.<o:p></o:p></span></p>

<p><span lang="EN-US">The first problem is the way the SQL is constructed, but
let's ignore that for now. What if the user types in a username such as "<st1:city w:st="on"><st1:place w:st="on">Alice</st1:place></st1:city>'--"? Assuming
a user named "<st1:city w:st="on"><st1:place w:st="on">Alice</st1:place></st1:city>"
exists in <span class="SpellE">SecurityTable</span>, the user (or more
appropriately the "hacker") successfully logs in. I'll leave finding
out why this happens as an exercise for you.<o:p></o:p></span></p>

<p><span lang="EN-US">Some creative client-side validation can prevent typical
users from doing this from the browser. But what about the case where
JavaScript is disabled on the client or for those advanced users (or hackers)
who can use another browser-like program to send direct commands (HTTP POST and
GET commands)? Server-side validation is a must to prevent this type of
exploitation. <span class="GramE">SSL,</span> firewalls, and the like won't help
you here.<o:p></o:p></span></p>

<p><span class="subhead"><b style=""><span lang="EN-US">Lesson
2: Security is Not an Add-On</span></b></span><span lang="EN-US"><br>
As I mentioned in Lesson 1, I have had the privilege of examining many Web
applications. A common theme I see is that all <span class="SpellE">JavaServer</span>
Pages (JSP) pages have a layout similar to this pseudo-code:<o:p></o:p></span></p>

<pre><span lang="EN-US">&lt;%<o:p></o:p></span></pre><pre><span lang="EN-US">User <span class="SpellE">user</span> = <o:p></o:p></span></pre><pre><span lang="EN-US"><span style="">&nbsp;&nbsp;</span><span class="SpellE"><span class="GramE">session.getAttribute</span></span><span class="GramE">(</span>"User");<o:p></o:p></span></pre><pre><span class="GramE"><span lang="EN-US">if(</span></span><span lang="EN-US">user == null)<o:p></o:p></span></pre><pre><span lang="EN-US">{<o:p></o:p></span></pre><pre><span lang="EN-US"><span style="">&nbsp;&nbsp; </span><span style="">&nbsp;&nbsp;&nbsp;&nbsp; </span>// redirect to <o:p></o:p></span></pre><pre><span lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;</span><span style="">&nbsp;&nbsp;&nbsp;&nbsp; </span>// the logon page…<o:p></o:p></span></pre><pre><span lang="EN-US">}<o:p></o:p></span></pre><pre><span lang="EN-US"><o:p>&nbsp;</o:p></span></pre><pre><span class="GramE"><span lang="EN-US">if(</span></span><span lang="EN-US">!<span class="SpellE">user.role.equals</span>("manager"))<o:p></o:p></span></pre><pre><span lang="EN-US">{<o:p></o:p></span></pre><pre><span lang="EN-US"> <span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// redirect to the<o:p></o:p></span></pre><pre><span lang="EN-US"><span style="">&nbsp; </span><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// "unauthorized" page…<o:p></o:p></span></pre><pre><span lang="EN-US">}<o:p></o:p></span></pre><pre><span lang="EN-US">%&gt;<o:p></o:p></span></pre><pre><span lang="EN-US"><o:p>&nbsp;</o:p></span></pre><pre><span lang="EN-US">&lt;!-<o:p></o:p></span></pre><pre><span lang="EN-US">HTML, JavaScript, and JSP<o:p></o:p></span></pre><pre><span class="GramE"><span lang="EN-US">code</span></span><span lang="EN-US"> to display data and<o:p></o:p></span></pre><pre><span class="GramE"><span lang="EN-US">allow</span></span><span lang="EN-US"> user interaction --&gt;<o:p></o:p></span></pre>

<p><span lang="EN-US">If the project uses an MVC framework such as Struts, all
Action Beans have similar code as well. While ultimately this code works fine,
it presents a maintenance nightmare if, for example, you find a bug or you must
add a new role (such as "guest" or "admin"). <o:p></o:p></span></p>

<p><span lang="EN-US">Furthermore, all developers, no matter how junior, need to
be familiar with this coding pattern. Sure, you can clean up JSP code with some
JSP tags, and you can create a base Action Bean that cleans up the derived
Action Beans. Even so, the maintenance nightmares still remain because the
security-related code is spread out in multiple places. The Web application is
also more likely to contain vulnerabilities because security is enforced at the
application code level (by multiple developers) rather than at the architecture
level.<o:p></o:p></span></p>

<p><span lang="EN-US">More likely, the underlying problem is that security was
slapped onto the project near the end. I recently worked as the architect on a
project to be implemented in six releases over the course of more than a year,
and security wasn't even mentioned until the fourth release—even though the
project was exposing highly sensitive personal data over the Web. We engaged in
a battle with the project sponsors and their management to change the release
schedule to include all security-related functionality in Release 1 and move
some of the "business" capability into subsequent releases. We
ultimately won. We also have a happy client because it has an extremely secure
application that protects its customers' private data, a fact in which it takes
great pride.<o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">In most applications, unfortunately, security does not appear to add any
real business value, so it gets swept under the rug until the end. When this
happens, security-related code just gets bolted on without any consideration of
the solution's long-term maintainability or robustness. Another symptom of this
security neglect is the absence of comprehensive server-side validation, which,
as I illustrated in Lesson 1, is an important part of a secure Web application.
<o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">Remember, security in a J2EE Web application is not just about using the
proper declarations in the <span class="SpellE">web.xml</span> or <span class="SpellE">ejb-jar.xml</span> file, or about using J2EE technologies such as
Java Authentication and Authorization Service (JAAS). It is about having a
well-thought-out plan and then implementing an architecture that supports it. <o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><b style=""><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">Lesson 3: I18N is Not a Just a Buzzword Anymore</span></b><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><br>
The reality of today's world is that non-native English speakers will access
your public Web application. This is especially true with e-government
initiatives that allow constituents (residents of a state) to interact with
their governmental agencies online. Examples include renewing your driver's
license or vehicle registration. Many people whose primary language is not
English will likely access such applications. Internationalization (or
"i18n" because there are 18 characters between the "<span class="SpellE">i</span>" and the "n" in
"internationalization") enables your application to work in multiple
languages.<o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">Obviously if you have hard-coded text in your JSP pages, or if your Java
code returns hard-coded error messages, then you will have a tough time
creating a Spanish version of your Web application. However, text is not the
only piece that must be "externalized" in a Web application that
supports multiple languages. Graphics and images should also be configurable
because many images have text embedded in them. In extreme cases, images (or
colors) that mean one thing in one culture portray a completely different
meaning in another culture. Similarly, any Java code that formats numbers and
dates must be localized. But, here's the biggie: Your page layout might require
change as well. <o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">For example, if you use HTML tables to format and display your menu
options, application headers, or footers, then you might have to change the
column widths at a minimum and possibly some other aspects of the table for
each supported language. To accommodate for varying colors and fonts, you might
have to use a separate <span class="SpellE">stylesheet</span> for each language. <o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">It should be obvious by now that creating an "<span class="SpellE">internationalizable</span>"
Web application is an architectural challenge rather than an application
challenge. A well-architected Web application means that your JSP pages and all
business-related (application-specific) Java code are oblivious to the selected
locale. The moral here: Don't take internationalization for granted just
because Java and the J2EE platform support it. You must architect your solution
with internationalization in mind from day one.<o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><b style=""><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">Lesson 4: Avoid Common Mistakes <span class="GramE">With</span>
MVC Presentation</span></b><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><br>
J2EE development has matured enough that most projects use some form of an MVC
architecture, such as Struts, on the presentation tier. A common theme I see in
such projects is the misuse of the MVC pattern. Here are a few examples. <o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">A common misuse is that all the business logic is implemented in the model
layer (for example, in the Action Beans in Struts). Remember that the
presentation layer's model layer is <i>still</i> part of the presentation
layer. The proper way to use this model layer is to call the appropriate
business layer services (or objects) and forward the results to the view layer.
In design pattern terms, the MVC presentation layer's model should be
implemented as a Façade for the business layer. Better yet, use the Business
Delegate pattern discussed in <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0131422464/ftponline-20" target="_blank"><i>Core J2EE Patterns</i></a>. This excerpt from the book
elegantly summarizes the gist and benefits of implementing your model as a
Business Delegate:<o:p></o:p></span></p>

<p class="MsoNormal" style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">The Business Delegate acts as a client-side business
abstraction; it provides an abstraction for, and thus hides, the implementation
of the business services. Using a Business Delegate reduces the coupling
between presentation-tier clients and the system's business services. Depending
on the implementation strategy, the Business Delegate may shield clients from
possible volatility in the implementation of the business service API.
Potentially, this reduces the number of changes that must be made to the
presentation-tier client code when the business service API or its underlying
implementation changes. <o:p></o:p></span></p>

<p><span lang="EN-US">Another common mistake is putting a lot of
presentation-type logic in the model layer. For example, if the JSP page needs
the date formatted in a specific way or the data ordered in a specific manner, some
would place that logic in the model layer, which is the wrong place for this
logic. It should actually be in a set of helper classes the JSP pages use. The
Action Bean should forward the data to the view layer as the business layer
returns it. This allows flexibility in supporting multiple view layers (JSP,
Velocity, XML, and so on) without creating unnecessary coupling between the
model and the view. It also allows the view to decide the best way to display
the data to the user. <o:p></o:p></span></p>

<p><span lang="EN-US">Finally, most MVC applications I've seen have an
under-utilized controller. For example, most Struts applications will create a
base Action class and perform all security-related functions there. All other
Action Beans are derived from this base class. This functionality should be
part of the controller because if the security conditions are not met, then the
call should never reach the Action Bean (or model) in the first place.
Remember, one of the most powerful features of well-designed MVC frameworks is
the presence of a robust and extensible controller. You should leverage this
power to your advantage.<o:p></o:p></span></p>

<p><span class="subhead"><b style=""><span lang="EN-US">Lesson
5: Don't Be Embarrassed by <span class="SpellE">POJOs</span></span></b></span><span lang="EN-US"><br>
I have witnessed many projects that use Enterprise JavaBeans for the sake of
using Enterprise JavaBeans. Sometimes it's the coolness factor, because <span class="SpellE">EJBs</span> appear to give the project an air of superiority and
self-importance. At other times it arises from confusion about the difference
between J2EE and EJB. Remember that EJB and J2EE are not synonyms. EJB is only
one part of J2EE. J2EE is a set of many technologies, including JSP, <span class="SpellE">servlets</span>, Java Message Service (JMS), Java Database
Connectivity (JDBC), JAAS, Java Management Extensions (JMX), and <span class="SpellE">EJBs</span>. J2EE is also a set of guiding principles and patterns
on how to use these technologies together to create solutions. <o:p></o:p></span></p>

<p><span lang="EN-US">If you use <span class="SpellE">EJBs</span> when they are not
required, they can hurt your application's performance. <span class="SpellE">EJBs</span>
typically require a more demanding application server than a plain old Web
server. They typically consume more memory and CPU time because of all the
value-added services they provide. Many applications don't require these
services, and the application server consequently competes with the application
for resources.<o:p></o:p></span></p>

<p><span lang="EN-US">In some cases, unnecessary EJB use can even cause your
applications to break. For example, I recently came across an application
developed on an open source application server. The business logic was
encapsulated in a series of <span class="SpellE">stateful</span> session beans (<span class="SpellE">EJBs</span>). The developers had worked hard to completely disable
"<span class="SpellE">passivation</span>" of these beans in the
application server. The client wanted the application deployed in a commercial
application server that was part of the client's technology stack. This
application server did not allow turning <span class="SpellE">passivation</span>
off. In fact, the client did not want any changes to its corporate application
server settings. As a result, the vendor had a big problem on its hands. The
(almost) funny thing is that the vendor couldn't provide a good reason why it
even implemented the code as <span class="SpellE">EJBs</span> (and <span class="SpellE">stateful</span> session beans at that). Not only did the vendor
suffer from performance problems, but its application did not work at the
client site.<o:p></o:p></span></p>

<p><span lang="EN-US">Plain Old Java Objects, or <span class="SpellE">POJOs</span>,
are powerful alternatives to <span class="SpellE">EJBs</span> in Web
applications. They are lightweight and don't carry all the extra baggage
associated with <span class="SpellE">EJBs</span>. In my opinion, many EJB
benefits such as object pooling are overrated. Don't be embarrassed by <span class="SpellE">POJOs</span>; they are your friends. <o:p></o:p></span></p>

<p><span class="subhead"><b style=""><span lang="EN-US">Lesson
6: Data Access Does Not Mandate O/R Mapping</span></b></span><span lang="EN-US"><br>
All Web applications I have worked with that provided user value accessed data
from somewhere and hence required a data access layer. That does not mean all
the projects identified and delineated such a layer; it simply means that such
a layer existed either implicitly or explicitly. In the case of an implicit
data layer, the data layer was part of the business object layer (or business
services). This works for small applications, but it goes against generally
accepted architecture guidelines for larger projects. <o:p></o:p></span></p>

<p><span lang="EN-US">In general, a data access layer must meet or exceed these
four criteria:<o:p></o:p></span></p>

<p><b><span lang="EN-US">Enables transparency</span></b><span lang="EN-US"><br>
Business objects can use the data source without knowing the specific details
of the data source implementation. Access is transparent because the
implementation details are hidden inside the data access layer.<o:p></o:p></span></p>

<p><b><span lang="EN-US">Enables easier migration</span></b><span lang="EN-US"><br>
<span class="GramE">A</span> data access layer makes it easier for an application
to migrate to a different database implementation. The business objects have no
knowledge of the underlying data implementation, so the migration involves
changes only to the data access layer. Further, if you're employing a factory
strategy, you can provide a concrete factory implementation for each underlying
storage implementation. In that case, migrating to a different storage
implementation means providing a new factory implementation to the application.
<o:p></o:p></span></p>

<p><b><span lang="EN-US">Reduces code complexity in business objects</span></b><span lang="EN-US"><br>
<span class="GramE">Because</span> the data access layer manages all the data
access complexities, it simplifies the code in the business objects and other
data clients that use the data access layer. The data access layer, not the
business object, contains all implementation-related code (such as SQL
statements). Benefits include higher developer productivity, better
maintainability, and improved code readability.<o:p></o:p></span></p>

<p><b><span lang="EN-US">Centralizes all data access into a separate layer</span></b><span lang="EN-US"><br>
<span class="GramE">Because</span> all data access operations are now delegated
to the data access layer, you can view the separate data access layer as the
layer that can isolate the rest of the application from the data access
implementation. This centralization makes the application easier to maintain
and manage. <o:p></o:p></span></p>

<p><span lang="EN-US">Note that none of these criteria explicitly call out the
need for an O/R (object-to-relational) mapping layer. An O/R mapping layer,
typically created with an O/R mapping tool, provides an object look-and-feel to
a relational data structure. In my opinion, using O/R mapping is similar to
using <span class="SpellE">EJBs</span> on a project. In most cases, it is simply
not required. O/R mapping can become quite complex for a relational database
with even moderate levels of joins and many-to-many relationships. Add to that
the inherent complexity of O/R mapping solutions themselves, such as lazy
loading and caching, and you have introduced quite a bit of complexity (and
risk) to your project. <o:p></o:p></span></p>

<p><span lang="EN-US">To further support my point, I'll point out the many failed
attempts by Sun Microsystems to popularize its Entity Beans (an implementation
of O/R mapping), which has been plagued with problems since version 1.0. In
Sun's defense, some of the earlier problems involved vendors' implementations
of the EJB specification. This, in turn, speaks to the complexity of the Entity
Beans specification itself. As a result, most J2EE architects generally agree
that staying away from Entity Beans is a good idea. <o:p></o:p></span></p>

<p><span lang="EN-US">Most applications have a finite number of queries they run
on their data. An efficient way of accessing the data in such applications is
to implement a data access layer that exposes a series of services (or objects,
or APIs) that execute these queries. As I mentioned earlier, O/R mapping is
simply not required in such cases. O/R mapping works well when you require
query flexibility, but remember that this additional flexibility does not come
for free. <o:p></o:p></span></p>

<p><span lang="EN-US">As promised, I kept my distance from parroting cliché best
practices in this article. Instead, I focused and offered my opinions on the
most significant decisions every architect on a J2EE project must make.
Ultimately, you should remember that J2EE is not about any specific technology
or about how many acronyms you can force-fit into the solution. Rather, you
should use the right technology at the right place and right time, and follow
the guidelines and practices embodied within J2EE that are more important than
the technology itself.<o:p></o:p></span></p>

<p><span class="aboutauthor"><span lang="EN-US">About the Author </span></span><span lang="EN-US"><br>
<span class="SpellE">Tarak</span> <span class="SpellE">Modi</span> is a senior
specialist with <a href="http://www.northhighland.com/" target="_blank">North
Highland</a>, a management and technology consulting company. His professional
experience includes working with COM, MTS, COM+, .NET, J2EE, and CORBA. He is a
coauthor of <a href="http://www.amazon.com/exec/obidos/tg/detail/-/1861003757/ftponline-20" target="_blank"><i>Professional Java Web Services</i></a> (<span class="SpellE">Wrox</span>
Press, 2002). Visit his personal Web site at <a href="http://www.teknirvana.com/" target="_blank">http://www.tekNirvana.com</a>. <o:p></o:p></span></p>

<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>

</div>
<img src ="http://www.blogjava.net/mstar/aggbug/4877.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-05-19 18:34 <a href="http://www.blogjava.net/mstar/archive/2005/05/19/4877.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>