﻿<?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-优雅天平-文章分类-web services</title><link>http://www.blogjava.net/Victor/category/513.html</link><description>享受喧嚣 安于平静</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 23:17:40 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 23:17:40 GMT</pubDate><ttl>60</ttl><item><title>使用JAXB处理XML文档--先睹为快</title><link>http://www.blogjava.net/Victor/articles/27422.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Tue, 10 Jan 2006 08:46:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/27422.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/27422.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/27422.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/27422.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/27422.html</trackback:ping><description><![CDATA[<SPAN id=LblContent>转载于cnjsp.com<BR>作者：<A class=l2 href="mailto:boyofjava@sina.com" target=_blank>AYellow</A>&nbsp;&nbsp;[2002-04-06]<BR>关键词：Java，XML，JAXB，DTD，绑定模式（Binding&nbsp;Schema），验证（validate），marshal，unmarshal<BR><BR>JAXB以其方便的XML数据处理能力可能会引起你的兴趣。你可能还不了解JAXB是什么，想要知道它到底有什么好处，如果这是你需要的，你才会再花时间去细细的研究它，或者你只需要使用最基本的功能。然而Sun关于JAXB的文档有80页之多。我想大部分人都没有耐心看完这样的长篇大论。本文以简短的篇幅介绍了JAXB的基本使用，算是先睹为快吧。本站提供<A class=l2 href="http://javaresearch.org/dn/jaxb-1_0-ea-bin.zip" target=_blank>JAXB1.0&nbsp;early&nbsp;access</A>版本的下载。欢迎与我讨论：&nbsp;mailto:boyofjava@sina.com<BR><BR>本文假设你会使用Java编程，了解并能够看懂XML，DTD。<BR><BR>
<H3>1&nbsp;为什么要使用JAXB</H3><BR>在Java中处理XML数据的常规方法有SAX，DOM等。其中SAX使用起来很麻烦，不能修改XML数据；而DOM的处理大文档速度非常的慢，易用性也不必SAX好到哪里去。实际上，无论是SAX还是DOM都不是专门为Java准备的，它们都是访问XML文档的统一底层接口，与语言无关。<BR>现在我们有了另外的选择。这就是JAXB和JDOM。JDOM与本文无关，目前最新的版本是beta8，感兴趣的话，可以访问http://www.jdom.org/。<BR>JAXB的全名是Java&nbsp;™&nbsp;Architecture&nbsp;for&nbsp;XML&nbsp;Binding，目前是1.0的early&nbsp;access版本，在Sun的Java站点只有注册为成员才能够下载。JAXB的特点就是将你用DTD定义好的XML文档映射为Java对象，提供简单、快速的数据操作方式。要访问XML中的元素、属性只要通过相应对象上的一系列getter和&nbsp;setter方法。你还可以通过marshal方法将对象的数据写进XML文件，通过unmarshal方法将XML文件的数据读入对象，通过validate方法验证XML文件是否符合DTD的约束。JAXB的缺点就在于只能访问特定的（也就是你用DTD定义的）XML文档。<BR><BR>
<H3>2&nbsp;JAXB如何工作</H3><BR>JAXB包括了一个运行类库和一个模式编译器。首先你要定义XML的DTD，然后编写一个绑定模式（Binding&nbsp;Schema）。DTD定义了XML文档，绑定模式也是一个XML文件，指出DTD定义的XML文档如何被映射为Java对象。运行编译器，将DTD和绑定模式作为参数传给编译器，编译器就会生成Java代码。编译生成的Java代码，通过这些代码就可以访问XML文档了。<BR><BR>
<H3>3&nbsp;JAXB的安装</H3><BR>以1.0&nbsp;early&nbsp;access为例，它不包含在JDK中，先到http://java.sun.com/xml下载。注意由于是早期版本，需要先登录才能下载，本文附带的源码包含了JAXB1.0&nbsp;early&nbsp;access。下载后将文件解压缩，在lib目录中有两个文件。jaxb-rt-1.0-ea.jar是运行支持库，jaxb-xjc-1.0-ea.jar是模式编译器。注意bin目录中的xjc文件只能在UNIX下使用，如果你的系统是Windows，那么你需要在命令行窗口手工输入命令来编译。为了在任何地方都可以运行模式编译器和它生成的代码，我们要把这两的文件加入CLASSPATH。一个简单的办法是把这两个文件拷贝到jre/lib/ext下。<BR><BR>
<H3>4&nbsp;一个简单的例子</H3><BR>有这样一个XML文档。它描述书的列表，举例如下：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><I><FONT color=#339900>//exampleA.xml</FONT></I> 
<LI>
<LI>&lt;?xml&nbsp;version=<FONT color=#ff33ff>"1.0"</FONT>&nbsp;encoding=<FONT color=#ff33ff>"GBK"</FONT>?&gt; 
<LI>&lt;bookList&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;book&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;Java编程入门&lt;/name&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;张三&lt;/author&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-6-6&lt;/publishDate&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;price&gt;35.0&lt;/price&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;book&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;XML在Java中的应用&lt;/name&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;李四&lt;/author&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-9-16&lt;/publishDate&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;price&gt;92.0&lt;/price&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/book&gt; 
<LI>&lt;/bookList&gt; </LI></OL></DIV><BR>其DTD文件如下：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><I><FONT color=#339900>//bookList.dtd</FONT></I> 
<LI>
<LI>&lt;!ELEMENT&nbsp;bookList&nbsp;(book)*&gt; 
<LI>&lt;!ELEMENT&nbsp;book(name,author,publishDate,price)&gt; 
<LI>&lt;!ELEMENT&nbsp;name&nbsp;(#PCDATA)&gt; 
<LI>&lt;!ELEMENT&nbsp;author&nbsp;(#PCDATA)&gt; 
<LI>&lt;!ELEMENT&nbsp;publishDate&nbsp;(#PCDATA)&gt; 
<LI>&lt;!ELEMENT&nbsp;price&nbsp;(#PCDATA)&gt; </LI></OL></DIV><BR>现在我们就来编写一个最简单的绑定模式，其文件扩展名应该为xjs。<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><I><FONT color=#339900>//bookList.xjs</FONT></I> 
<LI>
<LI>&lt;xml-java-binding-schema&nbsp;version=<FONT color=#ff33ff>"1.0-ea"</FONT>&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;element&nbsp;name=<FONT color=#ff33ff>"bookList"</FONT>&nbsp;type=<FONT color=#ff33ff>"class"</FONT>&nbsp;root=<FONT color=#ff33ff>"true"</FONT>/&gt; 
<LI>&lt;/xml-java-binding-schema&gt; </LI></OL></DIV><BR>现在就可以运行模式编译器生成Java代码，请先保证CLASSPATH中包含了JAXB的两个JAR文件。Windows用户注意bin目录下的那个文件是没用的。在命令行运行：<BR>
<DIV class=codeStyle>
<OL>
<LI>java&nbsp;com.sun.tools.xjc.Main&nbsp;bookList.dtd&nbsp;bookList.xjs </LI></OL></DIV><BR>如果没出问题，编译器就生成了Book.java，BookList.java两个文件。你不用去理解这两个源文件里面的代码，只要知道怎么使用它们提供的方法就可以了。它们的继承结构都是这样的：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI>java.lang.<B><A href="http://kb.csdn.net/source/jdk142/java/lang/Object.java.html" target=_blank><FONT class=classLink><U>Object</U></FONT></A></B> 
<LI>&nbsp;&nbsp;&nbsp;javax.xml.bind.ValidatableObject 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.xml.bind.MarshallableObject 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.xml.bind.MarshallableRootElement 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BookList&nbsp;or&nbsp;<FONT color=#ff0000>Book</FONT> </LI></OL></DIV><BR>BookList.java主要包含了以下方法<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI>BookList()&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//构造函数</FONT></I> 
<LI><FONT color=#ff0000>List</FONT>&nbsp;getBook()<I><FONT color=#339900>//得到书的集合，List中的对象实际类型是Book，可以添加、修改、删除其中的元素</FONT></I> 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;deleteBook()&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//删除集合</FONT></I> 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;emptyBook()&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//删除并生成一个新的空集合</FONT></I> 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;marshal(X)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//将数据写进XML文档</FONT></I> 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;unmarshal(X)&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//将数据从XML文档读入对象</FONT></I> 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;validate(X)&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//检查是否符合DTD约束，同时检查子树。在这个例子中就是BookList的Book集合</FONT></I> 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;validateThis()&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//检查是否符合DTD约束，不检查子树</FONT></I> </LI></OL></DIV><BR>其中marshal，unmarshal，validate被重载，有多种参数形式（可以参考JAXB的API文档）。<BR><BR><BR>Book.java主要包含了以下方法<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><FONT color=#ff0000>Book</FONT>() 
<LI><B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;getName() 
<LI><B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;getAuthor() 
<LI><B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;getPublishDate() 
<LI><B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;getPrice() 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;setName(<B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;x) 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;setAuthor(<B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;x) 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;setPublishDate(<B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;x) 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;setPrice(<B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;x) 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;marshal() 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;unmarshal() 
<LI><B><FONT color=#0000ff>void</FONT></B>&nbsp;validate() </LI></OL></DIV><BR>现在我们就可以使用这两个文件访问XML了。首先编译这两个文件。编写一个Test.java文件，把它和生成的两个文件以及前面的exampleA.xml放在一起。这个程序从&nbsp;exampleA.xml读入数据，作修改（把第一本书作者改成王五）后写入exampleB.xml。因为中文的编码问题，所以我们需要多一点手续。<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><I><FONT color=#339900>//Test.java</FONT></I> 
<LI>
<LI><B><FONT color=#0000ff>import</FONT></B>&nbsp;java.io.*; 
<LI><B><FONT color=#0000ff>import</FONT></B>&nbsp;java.util.*; 
<LI><B><FONT color=#0000ff>import</FONT></B>&nbsp;javax.xml.bind.*; 
<LI><B><FONT color=#0000ff>import</FONT></B>&nbsp;javax.xml.marshal.*; 
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;Test{ 
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><FONT color=#0000ff>void</FONT></B>&nbsp;main(<B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>[]&nbsp;args)&nbsp;<B><FONT color=#0000ff>throws</FONT></B>&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/lang/Exception.java.html" target=_blank><FONT class=classLink><U>Exception</U></FONT></A></B>{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BookList&nbsp;bl&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;BookList(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/io/FileInputStream.java.html" target=_blank><FONT class=classLink><U>FileInputStream</U></FONT></A></B>&nbsp;fis&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/io/FileInputStream.java.html" target=_blank><FONT class=classLink><U>FileInputStream</U></FONT></A></B>(<FONT color=#ff33ff>"exampleA.xml"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>try</FONT></B>{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bl&nbsp;=&nbsp;bl.unmarshal(fis); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<B><FONT color=#0000ff>finally</FONT></B>{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fis.close(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/util/List.java.html" target=_blank><FONT class=classLink><U>List</U></FONT></A></B>&nbsp;books&nbsp;=&nbsp;bl.getBook(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>Book</FONT>&nbsp;b&nbsp;=&nbsp;(<FONT color=#ff0000>Book</FONT>)books.get(0); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b.setAuthor(<FONT color=#ff33ff>"王五"</FONT>); 
<LI>
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bl.validate();&nbsp;&nbsp;<I><FONT color=#339900>//先验证，不然marshal会出错</FONT></I> 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/io/FileOutputStream.java.html" target=_blank><FONT class=classLink><U>FileOutputStream</U></FONT></A></B>&nbsp;fos&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/io/FileOutputStream.java.html" target=_blank><FONT class=classLink><U>FileOutputStream</U></FONT></A></B>(<FONT color=#ff33ff>"exampleB.xml"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XMLWriter&nbsp;xw&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;XMLWriter(fos,<FONT color=#ff33ff>"GBK"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>try</FONT></B>{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bl.marshal(xw); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<B><FONT color=#0000ff>finally</FONT></B>{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fos.close(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>} 
<LI>} </LI></OL></DIV><BR>编译运行，生成的文件exampleB.xml如下：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI>&lt;?xml&nbsp;version=<FONT color=#ff33ff>"1.0"</FONT>&nbsp;encoding=<FONT color=#ff33ff>"GBK"</FONT>?&gt; 
<LI>
<LI>&lt;bookList&gt; 
<LI>&nbsp;&nbsp;&lt;book&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;Java编程入门&lt;/name&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;王五&lt;/author&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-6-6&lt;/publishDate&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;price&gt;35.0&lt;/price&gt;&lt;/book&gt; 
<LI>&nbsp;&nbsp;&lt;book&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;XML在Java中的应用&lt;/name&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;author&gt;李四&lt;/author&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;publishDate&gt;2002-9-16&lt;/publishDate&gt; 
<LI>&lt;price&gt;92.0&lt;/price&gt;&lt;/book&gt;&lt;/bookList&gt; </LI></OL></DIV><BR>
<H3>5&nbsp;更进一步：数据类型转换</H3><BR>你可能已经注意到在上面的例子中，生成的Book对象的getPrice方法返回的是String，实际上它应该是float。同样publishDate以该是日期类型，而不是字符串。这是因为我们的绑定模式写得太简单了，模式编译器生成了默认的String类型。现在我们这样写：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><I><FONT color=#339900>//bookList2.xjs</FONT></I> 
<LI>
<LI>&lt;xml-java-binding-schema&nbsp;version=<FONT color=#ff33ff>"1.0-ea"</FONT>&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;element&nbsp;name=<FONT color=#ff33ff>"bookList"</FONT>&nbsp;type=<FONT color=#ff33ff>"class"</FONT>&nbsp;root=<FONT color=#ff33ff>"true"</FONT>/&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;element&nbsp;name=<FONT color=#ff33ff>"price"</FONT>&nbsp;type=<FONT color=#ff33ff>"value"</FONT>&nbsp;convert=<FONT color=#ff33ff>"float"</FONT>/&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;element&nbsp;name=<FONT color=#ff33ff>"publishDate"</FONT>&nbsp;type=<FONT color=#ff33ff>"value"</FONT>&nbsp;convert=<FONT color=#ff33ff>"TransDate"</FONT>&nbsp;/&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;conversion&nbsp;name=<FONT color=#ff33ff>"TransDate"</FONT>&nbsp;type=<FONT color=#ff33ff>"java.util.Date"</FONT> 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parse=<FONT color=#ff33ff>"TransDate.parseDate"</FONT>&nbsp;print=<FONT color=#ff33ff>"TransDate.printDate"</FONT>/&gt; 
<LI>&lt;/xml-java-binding-schema&gt; </LI></OL></DIV><BR>用java&nbsp;com.sun.tools.xjc.Main&nbsp;bookList.dtd&nbsp;bookList2.xjs运行编译器。生成的Book文件的相应代码为：<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><B><FONT color=#0000ff>float</FONT></B>&nbsp;getPrice() 
<LI>java.util.<B><A href="http://kb.csdn.net/source/jdk142/java/util/Date.java.html" target=_blank><FONT class=classLink><U>Date</U></FONT></A></B>&nbsp;getPublishDate() </LI></OL></DIV><BR>bookList2.xjs第3行将Price转换成了float类型，float类型是一个简单类型，因此用convert="float"描述就可以了。而&nbsp;publishDate需要转变成java.util.Date，这是一个类，而且他没有以字符串作为参数的构造函数。parse="TransDate.parseDate"就表示使用unmarshal读取数据的时候，会调用TransDate.parseDate()方法。这个静态方法以字符串为参数，返回java.util.date。print="TransDate.printDate"的作用相反。TransDate这个类需要我们提供。<BR>
<DIV class=codeStyle>
<OL>
<LI>
<LI><I><FONT color=#339900>//TransDate.java</FONT></I> 
<LI>
<LI><B><FONT color=#0000ff>import</FONT></B>&nbsp;java.util.<B><A href="http://kb.csdn.net/source/jdk142/java/util/Date.java.html" target=_blank><FONT class=classLink><U>Date</U></FONT></A></B>; 
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;TransDate&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>private</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;java.text.<B><A href="http://kb.csdn.net/source/jdk142/java/text/SimpleDateFormat.java.html" target=_blank><FONT class=classLink><U>SimpleDateFormat</U></FONT></A></B>&nbsp;df&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;java.text.<B><A href="http://kb.csdn.net/source/jdk142/java/text/SimpleDateFormat.java.html" target=_blank><FONT class=classLink><U>SimpleDateFormat</U></FONT></A></B>(<FONT color=#ff33ff>"yyyy-MM-dd"</FONT>); 
<LI>
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/util/Date.java.html" target=_blank><FONT class=classLink><U>Date</U></FONT></A></B>&nbsp;parseDate(<B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;d)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>try</FONT></B>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>return</FONT></B>&nbsp;df.parse(d); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<B><FONT color=#0000ff>catch</FONT></B>&nbsp;(java.text.<B><A href="http://kb.csdn.net/source/jdk142/java/text/ParseException.java.html" target=_blank><FONT class=classLink><U>ParseException</U></FONT></A></B>&nbsp;pe)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/lang/System.java.html" target=_blank><FONT class=classLink><U>System</U></FONT></A></B>.out.print(pe); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>return</FONT></B>&nbsp;<B><FONT color=#0000ff>new</FONT></B>&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/util/Date.java.html" target=_blank><FONT class=classLink><U>Date</U></FONT></A></B>(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><A href="http://kb.csdn.net/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;printDate(<B><A href="http://kb.csdn.net/source/jdk142/java/util/Date.java.html" target=_blank><FONT class=classLink><U>Date</U></FONT></A></B>&nbsp;d)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>return</FONT></B>&nbsp;df.format(d); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>
<H3>6&nbsp;那些使JAXB能够做到，但本文没有提到的</H3><BR>本文提供的这个例子很简单，实际上JAXB还可以定义文档的哪些元素（属性）可以被转换成类，哪些被转换成类的属性。处理元素的属性。处理枚举值。为一些元素共同的子元素生成接口（因为JAXB不支持NameSpace），定义继承结构等等。<BR><BR>
<H3>7&nbsp;JAXB不能做到的</H3><BR>Sun的文档里提到的：<BR>仅支持用DTD定义XML<BR>不支持NameSpace<BR>不支持内部子集、NOTATIONs、ENTITY、ENTITIES等。<BR><BR>另外，我发现如果要写一条处理指令到XML文档中，例如指定转换的样式单<BR>&lt;?xml-stylesheet&nbsp;href=”a.xsl”&nbsp;type=”text/xsl”?&gt;<BR>在JAXB中好像做不到，在javax.xml.marshal.XMLWriter中有一个chars(String&nbsp;str)方法，可以把字符串到XML文件的声明后面，但是这个方法对特殊字符作了转义，也就是没办法可以做到。这很奇怪，因为这是一个常用的功能，要实现也不难。也许还有我没有发现的办法。倒是有一个doctype方法可以写DOCTYPE声明。<BR></SPAN><img src ="http://www.blogjava.net/Victor/aggbug/27422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2006-01-10 16:46 <a href="http://www.blogjava.net/Victor/articles/27422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAXM开发Web服务的构架和模式(good2)</title><link>http://www.blogjava.net/Victor/articles/24248.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 16 Dec 2005 08:56:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/24248.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/24248.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/24248.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/24248.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/24248.html</trackback:ping><description><![CDATA[<P>级别: 初级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#author"><FONT color=#996699>陈亚强</FONT></A>, 高级软件工程师<BR></P>
<P>2003 年 6 月 01 日</P>
<BLOCKQUOTE>本文是本系列的第二篇，前一篇我们介绍了JAXM的开发技术，在这篇里，我将结合前一篇的案例来讨论JAXM Web服务的构架和设计模式。</BLOCKQUOTE>
<P>本文是本系列的第二篇， <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/index.shtml"><FONT color=#5c81a7>前一篇</FONT></A>我们介绍了JAXM的开发技术，在这篇里，我将结合前一篇的案例来讨论JAXM Web服务的构架和设计模式。 </P>
<P>阅读本文前您需要以下的知识和工具：</P>
<P>JavaTM Web Services Developer Pack 1.1，并且会使用初步使用；</P>
<P>至少会使用一种EJB容器来开发、部署EJB，并且会在客户端调用EJB组件；</P>
<P>对J2EE平台有比较全面的了解；</P>
<P>对UML比较熟悉。</P>
<P>本文的参考资料见 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#resources"><FONT color=#996699>参考资料</FONT></A> </P>
<P>本文的全部代码在这里 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#download"><FONT color=#996699>下载</FONT></A> </P>
<P><A name=1><SPAN class=atitle><STRONG><FONT size=4>系统构架</FONT></STRONG></SPAN></A></P>
<P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><STRONG><FONT size=4><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></STRONG></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><STRONG><FONT size=4></FONT></STRONG></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><STRONG><FONT face=Verdana color=#996699></FONT></STRONG></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></P>
<P><A name=2><SPAN class=atitle><STRONG><FONT size=4>消息传送方式</FONT></STRONG></SPAN></A></P>
<P><STRONG><BR><FONT size=4></FONT></STRONG></P>
<P>JAXM使用SOAP消息在消息客户端和服务端传送消息，消息有两种类型，一种是SOAPConnection，另一种是ProviderConnection。前者是一种点对点的消息发送模型，后者需要通过MessageProvider来把消息传送到目标。它们的消息传送路径如图1所示。</P><BR><A name=N1007A><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image001.gif"> <BR>
<P>图1 单向和双向的消息传送方式</P>
<P>不使用MessageProvider，可以带来一些便利，比如：</P>
<P>客户端可以是一般的J2SE程序（本文讲述的案例就是如此）；</P>
<P>不需要额外的配置。</P>
<P>但是，不使用MessageProvider也有以下的限制：</P>
<P>只能发送request-response类型的消息；</P>
<P>客户端只能是客户端的角色。</P>
<P>本文的案例采用了点对点的消息传送方式，调用环境如图2所示。</P><BR><A name=N100A1><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image002.gif"> <BR>
<P>图2 JAXM调用环境</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=3><SPAN class=atitle><FONT size=4>整体构架</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>系统体系结构如图3所示。</P><BR><A name=N100C1><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image003.gif"> <BR>
<P>图3 系统体系结构</P>
<P>上图的分层模型和J2EE应用程序的分层模型基本一致，不同的是客户端和JAXM Servlet数据的通信是封装成SOAP，但是，它也是HTTP的调用。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=4><SPAN class=atitle><FONT size=4>用例</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>本案例共有三个用例，它们分别是按名字查找书，按类别查找书，查找所有的书。如图4所示。</P><BR><A name=N100E4><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image004.gif"> <BR>
<P>图4 用例图</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=5><SPAN class=atitle><FONT size=4>数据模型</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>在数据库里，图书信息用图5的格式保存。</P><BR><A name=N10104><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image005.gif"> <BR>
<P>图5 数据库表</P>
<P>为了传输数据的方便，减少远程调用的次数，特别设计了BookVO来代表图书的信息，BookVO是一个值对象，如图6所示。</P><BR><A name=N10119><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image006.gif"> <BR>
<P>图6 BookVO值对象类图</P>
<P>值对象设计模式在J2EE模式中是非常有名且大量使用的设计模式，相信读者会很熟悉。我们知道，JAXM Servlet和EJB组件之间传递数据是通过对象来传递的，这个对象就是包含有BookVO实例的java.util.Collection。但是JAXM和客户端是通过SOAP消息来传递的（当然，也可以使用序列化的对象作为附件发送），为了传输图书信息，我们就要定义对应的DTD(或者schema)。针对以上的模型，定义的DTD如例程1所示。</P>
<P>例程1 传输图书信息的格式(book.dtd)</P>
<TABLE style="WIDTH: 725px; HEIGHT: 182px" cellSpacing=0 cellPadding=5 width=725 bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">&lt;!ELEMENT books(book*)&gt;
&lt;!ELEMENT book ( name, publisher, price, author+, category, description ) &gt;
&lt;!ELEMENT name (#PCDATA)&gt;
&lt;!ELEMENT publisher (#PCDATA)&gt;
&lt;!ELEMENT price (#PCDATA)&gt;
&lt;!ELEMENT author (#PCDATA)&gt;
&lt;!ELEMENT category (#PCDATA)&gt;
&lt;!ELEMENT description (#PCDATA)&gt;
&lt;!ATTLIST book  id  CDATA #REQUIRED&gt;
</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 style="WIDTH: 97.55%; HEIGHT: 2px" 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"><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=6><SPAN class=atitle><FONT size=4>业务逻辑</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>业务层是EJB组件，这里使用了两个EJB组件，一个是BookServiceFacadeEJB，它是一个有状态会话Bean，另一个是BookEntityEJB，它是一个实体Bean，代表了BookEntityTable的持久数据。</P>
<P>BookEntityEJB组件类图如图7所示。</P><BR><A name=N10147><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image007.gif"> <BR>
<P>图7 BookEntityEJB组件的类图</P>
<P>其中，isbn是BookEntityEJB组件的主键，BookEntityHome定义了几个find方法，它们是：</P>
<P>findAllBook()，查找所有的书，返回的是由BookEntity组成的Collection；</P>
<P>findByCategory(String category)，按类别查找书，返回的是由BookEntity组成的Collection；</P>
<P>findByName(String name)，按书名查找书，返回的是一个BookEntity的远程引用。</P>
<P>BookServiceFacadeEJB是会话门面，JAXM Servlet通过它来和BookEntityEJB交互。它的类图如下：</P><BR><A name=N10168><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image008.gif"> <BR>
<P>图8 BookServiceFacadeEJB组件的类图</P>
<P>同样，它也定义了几个find方法，但是和BookEntityHome接口不同的是，它返回的是包含了BookVO值对象的Collection，而不是包含了BookEntity远程引用的Collection。具体的实现细节请参考源代码。</P>
<P>EJB组件之间的依赖关系如图9所示。</P><BR><A name=N10180><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image0091.gif"> <BR>
<P>图9 EJB组件之间的依赖关系</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG style="WIDTH: 98.77%; HEIGHT: 1px" 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><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=7><SPAN class=atitle><FONT size=4>JAXM服务端</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>在JAXM 服务端设计了三个服务JAXM Servlet，分别对应了图4中的三个用例，它们是：</P>
<P>ListAllBook：查找所有的书；</P>
<P>BookDetail：按书名查找某本特定的图书；</P>
<P>ListByCategory：按类别查找。</P>
<P>它们的建模关系如图9所示。</P><BR><A name=N101AC><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image0092.gif"> <BR>
<P>图9 服务端JAXM Servlet的类图</P>
<P>每个JAXM Servlet都有一个SOAPMessage onMessage(SOAPMessage message)方法，这个方法在它们接收到SOAP消息时调用，可以说是客户端调用服务端的入口。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=8><SPAN class=atitle><FONT size=4>客户端</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>最终客户端是一个叫BookClientGUI的图形界面程序，它并不直接和SOAP消息打交道，它是通过JAXMDelegate类来和服务端JAXM Servlet进行交互的，这里我们简单列出它的类图，在接下来的设计模式里将详细介绍。</P><BR><A name=N101CF><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image010.gif"> <BR>
<P>图10 客户端类图</P>
<P>在上图中，请注意到BookBusiness接口，它是BookClientGUI和JAXMDelegate通信的桥梁，这里也体现了面向接口编程的思想。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG style="WIDTH: 98.91%; HEIGHT: 1px" 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><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=9><SPAN class=atitle><FONT size=4>设计模式</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>JAXM进行Web服务开发还不是特别普遍，故对它的设计模式的探讨还比较少。但是它也有它自己的特点，基本上来说，它的设计模式和J2EE平台其它组件设计模式是一致的。我们在使用J2EE设计模式时，基本上有以下几点的考虑：</P>
<UL>
<LI>减少远程调用的次数（使用值对象、值列表组装器、值对象组装器模式） 
<LI>降低组件之间、层（Tier）之间的耦合（使用会话门面、业务代表模式） 
<LI>减少服务查找的复杂度（使用服务定位器模式） 
<LI>数据的一致访问(使用数据访问对象模式) 
<LI>进行异步通信（使用服务激发器模式） </LI></UL>
<P>JAXM也是J2EE平台的一种技术，它当然可以使用J2EE核心模式中的任何一种，但是它有自己的特点，比如客户端和服务端是通过SOAP消息进行通信，这个和J2EE平台的其它组件之间通信是不同的。在JAXM编程中，为了实现数据（这里是SOAP消息）的一致返回，我们可以使用XML业务代表的模式。</P>
<P>JAXM进行编程时，数据传递的特点如图11所示。</P><BR><A name=N1020A><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image011.gif"> <BR>
<P>图11 JAXM数据传输的特点</P>
<P>从上图可以看出，客户端最终要使用的数据是java对象或者Java的基本数据类型，而客户端和服务端的通信是通过SOAP消息格式来传输的；同样，在服务端，它要调用业务逻辑，也必须使用java对象或者是java基本数据类型。这样就存在数据的传输和数据的使用的矛盾，为了解决这个矛盾，降低层（Tier）之间的耦合度，使数据易于处理，我们可以使用一个数据转换器来转换数据。当客户端要发送数据时，它使用数据转换器把请求数据转换成SOAP消息格式；在服务端，它调用了业务逻辑后，为了使数据能在internet上传输，它要使用数据转换器把调用结果封装成SOAP消息。接下来我们来看怎么处理这个问题。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG style="WIDTH: 98.09%; HEIGHT: 1px" 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><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=10><SPAN class=atitle><FONT size=4>客户端模式--JAXM业务代表</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>在客户端，通过使用JAXM业务代表，可以降低最终客户和SOAP消息的耦合度。系统的结构如下。</P><BR><A name=N1022D><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image012.gif"> <BR>
<P>图12 JAXM业务代表</P>
<P>JAXM业务代表使用数据转换器来转换数据，业务代表直接和Web服务进行交互，它屏蔽了Web服务请求的复杂过程，为客户端提供易于使用的接口。</P>
<P>此案例中，具体实现的类图如下：</P><BR><A name=N10245><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image013.gif"> <BR>
<P>图13 客户端类图</P>
<P>图中的JAXMDelegate为JAXM业务代表，它实现了BookBusiness接口，它是此模式的核心，它实现的方法是客户端可以直接调用的方法。</P>
<P>BookBusiness接口定义了和最终客户端（BookClientGUI）交互的方法，BookBusiness接口如例程2所示。</P>
<P>例程2 BookBusiness接口</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;

import java.util.Collection;
public interface BookBusiness 
{   
   /**
    * @return Collection，查询所有的书
    */
   public Collection getAllBooks();   
   /**
    * @param name
    * @return BookVO
    *查询某本特定的书
    */
   public BookVO getTheBookDetail(String name);
   
   /**
    * @return Collection
    *按类别查询图书 
*/
   public Collection getBookByCategory(String category);
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>SOAPToBeanEngine是数据转换器，它负责把具体的SOAP消息转换成客户端可以使用的数据。SOAPToBeanEngine实现了DTOEngine接口，我们看DTOEngine接口的具体代码，如例程3所示。</P>
<P>例程3 DTOEngine接口</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;

import java.util.Collection;
import javax.xml.soap.SOAPMessage;
public interface DTOEngine
{
 public void build();//把SOAP Message转换成Bean（对象）的具体代码。
 public Collection getResult();//返回转换结果。
 public void init(SOAPMessage msg);//初始化，msg为要转换的信息。 
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>以上三个方法是每个把SOAP消息转换成Java对象的数据转换器（如SOAPToBeanEngine）都必须实现的方法。实际上，这里的SOAPToBeanEngine只能转换BookVO相关的信息，如果要把此模式的框架设计得更加完美，还需进一步抽象，比如抽象到只要传入相关的值对象类(BookVO.class)和SOAP Message就能转换成对应的Bean结果集。</P>
<P>当客户端（BookClientGUI）发出一请求时，它调用JAXMDelegate对应的方法，JAXMDelegate根据请求构造对应的SOAP消息，然后把消息发送到服务端（如ListByCategory Servlet），服务端根据客户的请求做出对应的处理，并把处理结果返回到JAXMDelegate，JAXMDelegate使用SOAPToBeanEngine把返回的SOAP Message转化成Java对象（如值Bean），最后返回给客户端（BookClientGUI），BookClientGUI再把获得的数据进行处理后显示。</P>
<P>假如客户端要按类别查询图书信息，我们来看下一个顺序图，如图14所示。</P><BR><A name=N10279><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image014.gif"> <BR>
<P>图14 按类别查询图书客户端顺序图</P>
<P>JAXMDelegate是此模式的核心，我们来看一下它的代码，如例程4所示。</P>
<P>例程4 JAXMDelegate的部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;
import java.net.*;
import java.io.*;
import java.util.*;

import javax.xml.soap.*;

public class JAXMDelegate implements BookBusiness
{
 SOAPConnection con =null;//到服务端的连接
 EndpointLocator locator=new EndpointLocator();//服务定位器
 Collection allbook;//cache
 DTOEngine dto;//数据转换对象
 
 public JAXMDelegate()
 {
  allbook=new ArrayList();
  dto=new SOAPToBeanEngine();
  try
  {
   SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
           con = scf.createConnection();//生成一个用于SOAP调用的连接
        }
        catch(Exception e)
        {
         e.printStackTrace();
        }
    }
    //构造SOAP消息
    public SOAPMessage createMessage(String target,String name,String category) 
    {
     try
     {
       MessageFactory mf = MessageFactory.newInstance();
          SOAPMessage msg = mf.createMessage(); 
       SOAPPart sp = msg.getSOAPPart();   
   SOAPEnvelope envelope = sp.getEnvelope();   
   //SOAPHeader hdr = envelope.createSOAPHeader();
   //AttachmentPart attachment = message.createAttachmentPart();
   SOAPBody body = envelope.getBody();
    Name bodyName=envelope.createName(
"books",target,"http://hellking.webservice.com");
   SOAPBodyElement gpp=body.addBodyElement(bodyName);
   //如果category不空，那么构建一个按类别查找的SOAP消息
   if(category!=null)
   {
    gpp.addChildElement("category").addTextNode(category);
   }
   //如果name不空，那么构建一个按图书名字查找的SOAP消息
   if(name!=null)
   {
    gpp.addChildElement("name").addTextNode(name);
   } 
   msg.saveChanges();
   //msg.writeTo(new FileOutputStream("e://d.msg"));
   return msg;
  }
  catch(Exception e)
  {
   e.printStackTrace();
   return null;//一般要进行错误处理，这里省略
  }
  
 }
 //按类别查找书，业务代表方法
 public Collection getBookByCategory(String category)
 {
  try
  {
   SOAPMessage msg=createMessage("GetBookByCategory",null,category); 
         String endpoint=locator.getBookByCategory_Endpoint();
       SOAPMessage reply=con.call(msg,new URL(endpoint));
       reply.writeTo(System.out);
       dto.init(reply);//初始化数据转换器
       return dto.getResult();//返回数据转换结果
      
     }
     catch(Exception ex)
     {
      ex.printStackTrace();      
      return null;  //一般要进行错误处理，这里省略。    
     }
  }
 //查询所有的图书，业务代表方法  
    public Collection getAllBooks()
    {
     /**
*allbook为JAXMDelegate的cache，由于Web服务调用代价比较高，
*故使用它来减少不必要的远程调用。如果allbook为空，那么调用对应的Web服务
*来获得数据，并且把调用结果保存在allbook中，如果不为空，那么直接返回allbook
*中的数据。
*/
     if(allbook.size==0)
     {      
      try
      {
       SOAPMessage msg=createMessage("GetAllBooks",null,null);
       String endpoint=locator.getAllBooks_Endpoint();
       SOAPMessage reply=con.call(msg,new URL(endpoint));
       reply.writeTo(new FileOutputStream("e://out.msg"));
       dto.init(reply); //初始化数据转换器
       Collection re=dto.getResult();  //获得转换结果        
       allbook=re;
       return re; //返回数据转换结果
      }
      catch(Exception e)
      {
       e.printStackTrace();

       return null; //一般要进行错误处理，这里省略
      }   
   }
   else
   return allbook;     
    }
    //按图书名字查找某本图书，业务代表方法
    public BookVO getTheBookDetail(String name)
    {
     try
     {
      SOAPMessage msg=createMessage("GetBookDetail",name,null);
      String endpoint=locator.getTheBookDetail_Endpoint();
      SOAPMessage reply=con.call(msg,new URL(endpoint));
      reply.writeTo(System.out);
      dto.init(reply); //初始化数据转换器
      Collection ret=dto.getResult();      
      if(ret.size()==1)
      {
       return (BookVO)ret.iterator().next();
      }
      else
       return null;                   
     }
     catch(Exception e)
     {
      e.printStackTrace();
      return null; //一般要进行错误处理，这里省略
     }    
  } 
}
</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 style="WIDTH: 98.5%; HEIGHT: 1px" 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"><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=11><SPAN class=atitle><FONT size=4>服务端模式</FONT></SPAN></A></P>
<P><FONT size=4><BR></FONT></P>
<P>服务端的模式和客户端的模式基本一样，只是处理过程相反。服务端从客户端接收到SOAP消息后，然后读取参数，调用对应的业务方法，然后使用SOAPToBeanEngine来把调用的结果转换成SOAP消息返回。</P>
<P>如图15所示是相应的数据转换模型。</P><BR><A name=N102A7><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image015.gif"> <BR>
<P>图15所示是相应的数据转换模型</P>
<P>在服务端，数据转换器负责把对象转换成SOAP消息，这里和客户端是相反的。服务端类图如下。</P><BR><A name=N102BC><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image016.gif"> <BR>
<P>图16 服务端类图</P>
<P>在图16中，OTDEngine接口定义了把Bean转换成SOAP消息的方法，如例程5所示。</P>
<P>例程5 OTDEngine接口定义的方法</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;

import javax.xml.soap.*;
import java.util.Collection;
public interface OTDEngine
{
 public void build();//构造SOAP消息
 public SOAPMessage getResult();//返回结果
 public void init(Collection c,String type);//初始化 
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>OTDEngine定义了把Bean转换成SOAP消息需要的方法：build()、init()、getResult()。</P>
<P>XMLBusinessDelegate是此模式的核心，它调用业务逻辑，并且使用BeanToSOAPEngine来转换结果。我们来看它的部分代码，如例程6所示。</P>
<P>例程6 XMLBusinessDelegate部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;
import javax.naming.*;
import com.hellking.webservice.ejb.*;
import java.util.*;
import java.rmi.*;
import javax.xml.messaging.*;
import javax.xml.soap.*;
public class XMLBusinessDelegate
{
 InitialContext init=null;
 BookServiceFacadeHome facadeHome;
 OTDEngine otd;
 public XMLBusinessDelegate()throws NamingException
 {
   init=this.getInitialContext();
   otd=new BeanToSOAPEngine();        
 } 
 public static InitialContext getInitialContext() 
      throws javax.naming.NamingException 
   {
       Properties p = new Properties();
        //…  p.put(XXX,XXX)
       return new javax.naming.InitialContext(p);
   }
   //查找所有的图书
   public SOAPMessage listAllBook()
   {  
  try
  {
    //查找业务组件à调用业务逻辑à构造SOAP消息à返回消息。 
       Object objref = init.lookup("ejb/bookservicefacade");  
           facadeHome = (BookServiceFacadeHome)
javax.rmi.PortableRemoteObject.narrow(objref, BookServiceFacadeHome.class); 
   Collection result=facadeHome.create().getAllBook();
   System.out.println(result.size());
   otd.init(result,"GetAllBooks");//初始化BeanToSOAPEngine
   SOAPMessage ret=otd.getResult();//获得结果
   return ret;         
    }
    catch(Exception e)
    {
      e.printStackTrace();
      return null;
    }
   }
   //按Category查找图书
   public SOAPMessage listByCategory(String category)
   {
     try
  {
  //查找业务组件à调用业务逻辑à构造SOAP消息à返回消息。 
      Object objref = init.lookup("ejb/bookservicefacade");
  facadeHome = (BookServiceFacadeHome)
javax.rmi.PortableRemoteObject.narrow(objref, BookServiceFacadeHome.class); 
   Collection result=facadeHome.create().findByCategory(category);
   otd.init(result,"GetBookByCategory");//初始化BeanToSOAPEngine
   SOAPMessage ret=otd.getResult();//获得结果
   return ret;    //返回结果     
    }
    catch(Exception e)
    {
      e.printStackTrace();
      return null;
    }
   }   
    //查询某本特定的图书。
   public SOAPMessage getBookDetail(String name)
   {
     try
  {
  //查找业务组件à调用业务逻辑à构造SOAP消息à返回消息。 
    Object objref = init.lookup("ejb/bookservicefacade");  
        facadeHome = (BookServiceFacadeHome)
javax.rmi.PortableRemoteObject.narrow(objref, BookServiceFacadeHome.class); 
   Collection result=facadeHome.create().getBookDetail(name);
   otd.init(result,"GetBookDetail");
   SOAPMessage ret=otd.getResult();
   return ret;         
    }
    catch(Exception e)
    {
      e.printStackTrace();
      return null;
    }
   }  
…
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>假如客户端传来要按类别查询图书信息，ListByCategory Servlet将调用XMLBusinessDelegate 的listByCategory(String category)方法，XMLBusinessDelegate查找BookServiceFacadeHome接口，生成BookServiceFacade应用，调用getBookDetail(name);方法，然后初始化OTDEngine，最后调用getResult()方法来返回结果。顺序图如图17所示。</P><BR><A name=N102EA><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/image017.gif"> <BR>
<P>图17 按类别查找图书的服务端顺序图</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG style="WIDTH: 98.64%; HEIGHT: 1px" 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><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part2/#main"><B><FONT face=Verdana color=#996699></FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=12><SPAN class=atitle><FONT size=4>总结</FONT></SPAN></A></P>
<P>本篇结合具体的案例介绍了JAXM Web服务开发的体系结构和设计模式。数据转换在设计中占有很大的分量，总的来说，从客户端发出的数据要经过以下途径：</P>
<P>java数据类型àSOAP请求消息àjava数据类型à业务逻辑返回的java数据类型àSOAP相应消息àjava数据类型</P>
<P>业务代表模型在以上数据转换和业务处理起着重要的作用。</P></B></B></B></B></B></B></B></B></B></B></B></B></B></B></B></B></B></B><img src ="http://www.blogjava.net/Victor/aggbug/24248.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-16 16:56 <a href="http://www.blogjava.net/Victor/articles/24248.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用JAXM开发Web服务(good)</title><link>http://www.blogjava.net/Victor/articles/24238.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 16 Dec 2005 08:12:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/24238.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/24238.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/24238.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/24238.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/24238.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<P id=subtitle>J2EE Web服务开发系列之一</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small></TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8>&nbsp;
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16></TD>
<TD width=122>
<P>&nbsp;</P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small></TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P>&nbsp;</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 初级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#author"><FONT color=#996699>陈亚强</FONT></A>, 高级软件工程师<BR></P>
<P>2003 年 6 月 01 日</P>
<BLOCKQUOTE>本文介绍JAXM Web服务开发的基本概念，然后结合一个具体的案例来介绍使用JAXM开发Web服务中要使用的编程技术和编程技巧。</BLOCKQUOTE>
<P>阅读本文前您需要以下的知识和工具：</P>
<P>JavaTM Web Services Developer Pack 1.1，并且会使用初步使用；</P>
<P>至少会使用一种EJB容器来开发、部署EJB，并且了解怎么在客户端访问EJB组件；</P>
<P>一般的Java编程知识。</P>
<P>在J2EE平台里，要开发一个Web服务，我们通常有两种选择：</P>
<P>使用JAX-RPC（Java API for XML-based RPC）</P>
<P>使用JAXM（Java API for XML Messaging）</P>
<P>作为对JAXM开发技术的入门，本文先不比较它们的技术特点。我将结合一个具体的案例来讨论JAXM的开发技术方方面面。</P>
<P>本文的参考资料见 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#resources"><FONT color=#996699>参考资料</FONT></A> </P>
<P>本文的全部代码在这里 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#download"><FONT color=#996699>下载</FONT></A> </P>
<P><A name=1><SPAN class=atitle><FONT face=Arial size=4>JAXM相关概念介绍</FONT></SPAN></A></P>
<P><BR>通常我们说的JAXM API，它包括两个包： </P>
<P>Javax.xml.soap：它是发送SOAP消息的基本包，主要包含了发送带有附件的SOAP消息的API（SOAP with Attachments API for Java ，SAAJ）。它是SOAP消息的基本包，它为构建SOAP包和解析SOAP包提供了重要的支持。它包含了发送请求－响应消息相关的API。</P>
<P>Javax.xml.messaging：定义了JAXM的规范，包含了发送和接收消息所需的API。</P>
<P>JAXM包含了以下几个概念：消息（Message）、连接（Connection）、消息提供者（Messaging providers）。</P>
<P><A name=1.1><SPAN class=smalltitle><STRONG><FONT face=Arial>消息</FONT></STRONG></SPAN></A></P>
<P><BR>JAXM消息遵循SOAP标准，我们可以通过JAXM API方便的创建SOAP 消息。有两种类型的消息，带附件的消息和不带附加的消息。不带附件的消息结构如图1所示。 </P>
<P>如图1所示，在SAAJ API中，它使用SOAPMessage类来代表SOAPMessage，相应的，使用SOAPPart类来代表SOAPPart，SOAPBody类代表SOAP Body。</P><BR><A name=N10088><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/image001.gif"> <BR>
<P>图1 不带附件的SOAP消息</P>
<P>其中Header和SOAPFault是可选的，Header可以多个，Body只有一个，如果有SOAP Fault，那么它一定在SOAP Body后面。带附加的SOAP消息如图2所示。</P><BR><A name=N1009D><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/image002.gif"> <BR>
<P>图2 带附件SOAP消息</P>
<P>可以看出，一个SOAP消息可以有一个或者多个附件。SAAJ API使用AttachmentPart类来代表SOAP消息的附件。每个AttachmentPart有一个MIME Header来表示附件的类型。</P>
<P><A name=1.2><SPAN class=smalltitle><FONT face=Arial>连接</FONT></SPAN></A></P>
<P><BR>有两种类型的连接，它们是： </P>
<P>消息发送者到接收者的直接连接，javax.xml.soap.SOAPConnection表示了这种类型的连接，由于它是点对点的，所以比较容易使用，即使不在Servlet或者J2EE容器里也能使用； </P>
<P>到消息提供者的连接，javax.xml.messaging.ProviderConnection表示了这种连接，这种方式需要消息提供者，消息发送者和消息使用者通过消息提供者来交互。 </P>
<P><A name=1.3><SPAN class=smalltitle><FONT face=Arial>消息提供者</FONT></SPAN></A></P>
<P><BR>消息提供者主要负责传送消息，它把消息路由到目的地，一个消息发出后，可能要经过多个消息提供者才能到达目的地。 </P>
<P>如果使用MessageProvider，可以达到以下的目的： </P>
<P>除了能够发送request-response类型的消息外，还可以发送One-way（单向）消息； </P>
<P>（消息）客户端有时也可以作为服务端来使用。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=2><SPAN class=atitle><FONT face=Arial size=4>案例介绍</FONT></SPAN></A></P>
<P><BR>在本文，我将结合一个具体的案例来介绍JAXM Web服务的开发。此案例具体情况如下。 </P>
<P>某图书城决定使用Web服务来对外提供图书信息查询服务，图书城现有的系统运行在J2EE平台上，客户端通过JAXM来使用图书城提供的Web服务。系统的体系结构如图3所示： </P><BR><A name=N100E5><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/image003.gif"> <BR>
<P>图3 系统体系结构</P>
<P>客户端可以是一般的java GUI程序（当然也可以是JSP、Servlet等）。客户端通过SOAP消息和Servlet容器里运行的JAXM Servlet进行交互，JAXM Servlet是服务提供者，EJB容器里运行的是业务组件，它们为JAXM Servlet提供服务。 </P>
<P>客户端请求传递的过程如图4所示：</P><BR><A name=N100FD><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/image004.gif"> <BR>
<P>图4 请求传递的过程</P>
<P>可以看出，客户端通过SOAP和JAXM 服务端通信，JAXM使用EJB组件来获得业务服务。 </P>
<P>系统为客户端提供了三种查询服务：查询所有图书，按类别查询图书，按图书名搜索某本特定的图书。这三种服务分别有服务端的三个JAXM Servlet实现。它们是： </P>
<P>ListAllBook：查询所有的图书信息； </P>
<P>ListByCategory：按类别查询图书信息；</P>
<P>BookDetail：查询某个特定名称的图书信息。</P>
<P>客户端是用Swing编写的GUI界面，使用界面如图5所示。</P><BR><A name=N10121><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/image005.gif"> <BR>
<P>图5 客户端界面</P>
<P>客户端和服务端传输图书信息时采用例程1所示的格式。</P>
<P>例程1 传输图书信息的格式(book.dtd)</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">&lt;!ELEMENT books(book*)&gt;
&lt;!ELEMENT book (name,publisher,price,author+,category,description)&gt;
&lt;!ELEMENT name (#PCDATA)&gt;
&lt;!ELEMENT publisher (#PCDATA)&gt;
&lt;!ELEMENT price (#PCDATA)&gt;
&lt;!ELEMENT author (#PCDATA)&gt;
&lt;!ELEMENT category (#PCDATA)&gt;
&lt;!ELEMENT description (#PCDATA)&gt;
&lt;!ATTLIST book id  CDATA #REQUIRED&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这个信息包含在SOAP消息的Body里，按照这个格式，传输的SOAP消息结构如例程2所示。</P>
<P>例程2 传输的SOAP消息的格式（book.msg）</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">&lt;soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"&gt;
    &lt;soap-env:Header/&gt;
    &lt;soap-env:Body&gt;
        &lt;books:GetAllBooks xmlns:books="http://hellking.webservice.com"&gt;
           &lt;books:book id="2-1234-4455-4"&gt;
    &lt;books:name&gt;J2EE企业应用开发&lt;/books:name&gt;
    &lt;books:publisher&gt;电子工业出版社&lt;/books:publisher&gt;
    &lt;books:price&gt;60&lt;/books:price&gt;
    &lt;books:category&gt;计算机类&lt;/books:category&gt;
    &lt;books:description&gt;非常好的介绍J2EE企业应用开发的书&lt;/books:description&gt;
    &lt;books:author&gt;陈亚强&lt;/books:author&gt;
    &lt;books:author&gt;刘晓华&lt;/books:author&gt;
       &lt;/books:book&gt;
&lt;/books:GetAllBooks&gt;
   &lt;/soap-env:Body&gt;
&lt;/soap-env:Envelope&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>为了传输数据的便利，我把图书信息用一个专门的值对象来表示，如例程3所示。</P>
<P>例程3 BookVO值对象</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;
import java.util.Collection;
public class BookVO implements java.io.Serializable
{
   private String name;//图书名字
   private String publisher;//图书出版社
   private float price;//图书价格
   private String isbn;//图书ISBN
   private String description;//图书的简介
   private String category;//图书的类别
   private Collection authors;//图书的作者，因一本书可以有多个作者，故把它表示成Collection。
public void setName(String name)
   {
     this.name=name;
  }
public String getName()
  {
     return this.name;
  }
…其它的getter和setter方法
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>可以看出，BookVO其实是和例程1中的DTD是对应的。需要指出的是，BookVO可以使用JAXB（Java API for XML Binding）中的工具来生成。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=3><SPAN class=atitle><FONT face=Arial size=4>EJB组件介绍</FONT></SPAN></A></P>
<P><BR>本案例使用了两个EJB组件，它们分别是BookEntityEJB和BookServiceFacadeEJB。其中BookEntityEJB是实体Bean，它代表了每本书的详细信息；BookServiceFacadeEJB为有状态会话Bean，它是一个会话门面，为JAXM Servlet提供业务服务。BookServiceFacadeEJB组件的远程接口如例程4所示。 </P>
<P>例程4 BookServiceFacadeEJB组件的远程接口</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice.ejb;
import java.rmi.RemoteException;
import javax.ejb.*;

public interface BookServiceFacade extends EJBObject
{
    /**
     * @J2EE_METHOD  --  getAllBook，查找所有的书
     */
    public java.util.Collection getAllBook    () throws RemoteException;
    
    /**
     * @J2EE_METHOD  --  findByCategory，按类别查找
     */
    public java.util.Collection findByCategory    (String category) throws RemoteException;

/**
     * @J2EE_METHOD  --  getBookDetail   ，按名字查找
     */

    public java.util.Collection getBookDetail    (String name) throws RemoteException;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>可以看出，它提供了三个业务服务，分别是getAllBook()，findByCategory(String category)，getBookDetail(String name)。这三个业务方法返回的都是java.util.Collection。其实，getBookDetail( String name )方法返回的应该是一个值对象，但是为了方便统一处理，也通过处理让它返回java.util.Collection类型，这一点以后的代码中体现出来。 </P>
<P>在BookEntityEJB Home接口也提供了对应的查找方法，如例程5所示。</P>
<P>例程5 BookEntityEJB的Home接口</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice.ejb;

import java.rmi.RemoteException;
import javax.ejb.*;

public interface BookEntityHome extends EJBHome
{
    public BookEntity findByPrimaryKey    (String primaryKey) 
                throws RemoteException, FinderException;
   
    public BookEntity create(String isbn) throws RemoteException, CreateException;
    
    /**
     * @J2EE_METHOD  --  getAllBook，查找所有的书
     */
public java.util.Collection findAllBook    () 
                throws RemoteException, FinderException; 
  
/**
     * @J2EE_METHOD  --  findByCategory，按类别查找
     */

    public java.util.Collection findByCategory    (String category) 
                throws RemoteException, FinderException; 
   
/**
     * @J2EE_METHOD  --  getBookDetail   ，按名字查找
   */
 public BookEntity findByName    (String name) 
                throws RemoteException, FinderException; 
    
}
</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/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=4><SPAN class=atitle><FONT face=Arial size=4>开发服务端</FONT></SPAN></A></P>
<P><BR>下面开发服务端，我们前面说过，服务端共有三个JAXM Servlet，它们分别提供三种不同的查询服务。 </P>
<P>由于使用了点对点的消息模型，故服务端需要实现javax.xml.messaging. ReqRespListener接口，并且需要继承javax.xml.messaging.JAXMServlet类。javax.xml.messaging.JAXMServlet是一个Servlet，它为开发消息服务的Servlet提供了一个框架。需要指出的是，javax.xml.messaging. ReqRespListener接口定义了一个 </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">public SOAPMessage onMessage  (SOAPMessage message)
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>方法，故我们开发的JAXM服务端Servlet必须实现这个方法。 onMessage 方法就是当此Servlet接收到SOAPMessage时激发的方法，它通过此方法对外界提供服务（我们可以把这个方法简单的比喻成普通的HttpServlet中的doGet()、doPost()方法，HttpServlet正是通过doGet()、doPost()来为客户端提供服务）。 </P>
<P>ListAllBook的部分代码如例程6所示。 </P>
<P>例程6 ListAllBook的部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">public class ListAllBook extends JAXMServlet  implements ReqRespListener
{
    public void init    (ServletConfig servletConfig) throws ServletException 
    {
     super.init(servletConfig);
    }
    public SOAPMessage onMessage    (SOAPMessage message)  
    { 
  System.out.println("from ListAllBook Servlet:receive a message");  
  try
  {
   System.out.println("from ListAllBook Servlet:");
   message.writeTo(System.out);//在控制台打印收到的消息 
             //调用其它类来实现业务方法  
            SOAPMessage msg=new XMLBusinessDelegate().listAllBook();
            System.out.println("this is the reply.....");
            msg.writeTo(System.out);
            msg.saveChanges();//注意，在返回消息之前要调用这个方法
            return msg;//返回消息
        }
        catch(Exception ex)
        {
         ex.printStackTrace();
           //处理错误….
         return null;
        }
        
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在这个例子里，由于接收到的消息不含任何参数，故没有对它进行处理，一般情况下，接收的消息是有参数的，并且服务端需要使用这个参数来调用业务层组件，如当用户按类别查找图书时，服务端需要获得类别的名字，例程7是按类别查找图书的服务端Servlet的部分代码，我们看服务端是怎么获得客户端的请求参数。 </P>
<P>例程7 ListByCategory的部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">public SOAPMessage onMessage  (SOAPMessage message)  
    { 
  try
  {
      …
   SOAPEnvelope env=message.getSOAPPart().getEnvelope();
   Iterator it=env.getBody().getChildElements(
env.createName("books","GetBookByCategory","http://hellking.webservice.com"));
   SOAPElement books=(SOAPElement)it.next();
   
   Iterator it2=books.getChildElements(
env.createName("category","GetBookByCategory","http://hellking.webservice.com"));
   String category=((SOAPElement)it2.next()).getValue();
              //这里的category是从SOAP 消息中读出的参数
   SOAPMessage msg=new XMLBusinessDelegate().listByCategory(category);
             msg.saveChanges();
            return msg;//返回处理消息
        }
        catch(Exception ex)
        {
         ex.printStackTrace();
         //处理错误….
         return null;//如果出错，一般返回SOAPFault，这里简化了。
        }
    }
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>SAAJ API为解析SOAP 消息提供了很好的支持，注意上面的黑体子，它是读取获得查找图书类别参数category的代码，这个参数是客户端设置的，在以后我们将看到客户端怎么设置这个参数。 </P>
<P>总结一下，JAXM服务端Servlet处理消息的步骤是：</P>
<OL>
<LI>获得消息（onMessage） 
<LI>读取消息中需要的参数 
<LI>利用参数调用对应的业务处理 
<LI>构建响应SOAP消息 
<LI>返回处理后的消息 </LI></OL>
<P>从上面的例子可以看出，JAXM服务端Servlet并没有处理具体的业务，而是把业务处理交给一个叫XMLBusinessDelegate业务代表的类来处理。 </P>
<P>我们来看一下XMLBusinessDelegate是怎么来进行业务处理的。如例程8所示。 </P>
<P>例程8 XMLBusinessDelegate的部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">…
public class XMLBusinessDelegate
{
InitialContext init=null;
  BookServiceFacadeHome facadeHome;
OTDEngine otd;//对象到数据的转换器
 public XMLBusinessDelegate()throws NamingException
 {
   init=this.getInitialContext();
otd=new BeanToSOAPEngine(); //生成对象到数据转换器实例       
 }
 public static InitialContext getInitialContext()  throws javax.naming.NamingException 
   {
      //更据不同的EJB容器，使用不同的url和连接工厂来获得上下文，然后返回…
}
//业务方法，按类别查找图书
public SOAPMessage listByCategory(String category)
   {

     try
  {
   
    Object objref = init.lookup("ejb/bookservicefacade");  
        facadeHome = (BookServiceFacadeHome)
javax.rmi.PortableRemoteObject.narrow(objref, BookServiceFacadeHome.class); 
   System.out.println("call jboss======&gt;&gt;");
   Collection result=facadeHome.create().findByCategory(category);//调用业务方法
   System.out.println("get result======&gt;&gt;");
   System.out.println(result.size());
     // 使用BeanToSOAPEngine把调用结果转换成SOAP消息
otd.init(result,"GetAllBooks");
   SOAPMessage ret=otd.getResult();
   return ret;        
    }
    catch(Exception e)
    {
      e.printStackTrace();
      return null;
    }
   } 
… 
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>可以看出，XMLBusinessDelegate只是调用EJB组件的业务方法，然后把构建SOAP消息的任务交给BeanToSOAPEngine，BeanToSOAPEngine是负责把包含了BookVO 的Collection转换成SOAP消息的专门的类。BeanToSOAPEngine的部分代码如例程9所示。 </P>
<P>例程9 BeanToSOAPEngine的部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">…
public class BeanToSOAPEngine implements OTDEngine
{
 Collection bookVos;//要处理的信息
 SOAPMessage msg;//待返回的消息
 String type;//type为返回消息的名字空间，如GetBookByCategory
 public BeanToSOAPEngine()
 {  
  try
  {
   MessageFactory mf = MessageFactory.newInstance();//获得MessageFactory的实例
   msg = mf.createMessage();//从MessageFactory建立一个空的Message
  }
  catch(Exception ex)
  {
   ex.printStackTrace();
  } 
 }
 public void init()
 {
this.bookVos=c;
  this.type=type;
 }
 public SOAPMessage getResult()
 {
  build();
  return msg;
 }
 public void build()
 {  
  try
  {   
         SOAPPart part = msg.getSOAPPart();
         SOAPEnvelope envelope = part.getEnvelope();
         SOAPBody body = envelope.getBody();
            //创建body名字空间
         Name bodyName=envelope.createName(type,"books","http://hellking.webservice.com" );
     SOAPBodyElement books=body.addBodyElement(bodyName);//增加body
     
//以下程序把Collection中的BookVO转化成SOAP消息
Iterator it=bookVos.iterator();
      while(it.hasNext())
      {
       BookVO bookvo=(BookVO)it.next(); 
             
               //构建book      
       Name bookName=envelope.createName(
"book","books","http://hellking.webservice.com");
    SOAPElement book=books.addChildElement(bookName);
    book.addAttribute(envelope.createName("id"),bookvo.getIsbn());
    
    //构建name
Name elName=envelope.createName(
"name","books","http://hellking.webservice.com");
    book.addChildElement(elName).addTextNode(bookvo.getName());
    
    //构建publisher
    Name elPublisher=envelope.createName(
"publisher","books","http://hellking.webservice.com");
    book.addChildElement(elPublisher).addTextNode(bookvo.getPublisher());
    
    //构建price
    Name elPrice=envelope.createName(
"price","books","http://hellking.webservice.com");
    book.addChildElement(elPrice).addTextNode(
new Float(bookvo.getPrice()).toString());
    
               //构建category
    Name elCategory=envelope.createName(
"category","books","http://hellking.webservice.com");
    book.addChildElement(elCategory).addTextNode(bookvo.getCategory());

    //构建description
    Name elDescription=envelope.createName(
"description","books","http://hellking.webservice.com");
    book.addChildElement(elDescription).addTextNode(bookvo.getDescription());
    //author本来就是一个Collection，故要特别处理
    Collection author_c=bookvo.getAuthors();
    Iterator au_it=author_c.iterator();
    while(au_it.hasNext())
    {
     String strAuth=(String)au_it.next();
     //增加一个author
     Name elAuth=envelope.createName(
"author","books","http://hellking.webservice.com");
        book.addChildElement(elAuth).addTextNode(strAuth);
       }    
   }
   msg.writeTo(System.out); //打印消息 
  }
  catch(Exception ex)
  {
   ex.printStackTrace();
  }
}
…
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>按照上面的代码，BeanToSOAPEngine构建的SOAP消息应该和例程2的结构一致。</P>
<P>总结一下，构建SOAP消息时，按以下步骤进行：</P>
<OL>
<LI>获得MessageFactory 实例 
<LI>利用MessageFactory 创建空的SOAPMessage 
<LI>创建Header（可选） 
<LI>创建body 
<OL type=A>
<LI>创建body的名字空间 
<LI>创建body的子元素 
<OL type=a>
<LI>创建子元素名字空间 
<LI>循环创建子子元素 
<LI>把子元素增加到父元素里 </LI></OL>
<LI>往Message里增加body元素 </LI></OL>
<LI>创建附件（可选） </LI></OL><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/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=5><SPAN class=atitle><FONT face=Arial size=4>开发客户端</FONT></SPAN></A></P>
<P><BR>客户端和服务端通信，按以下的步骤进行： </P>
<OL>
<LI>创建 SOAP 连接 
<LI>创建 SOAP 消息 
<LI>在SOAP消息里增加数据 
<LI>发送消息 
<LI>对SOAP应答进行处理 </LI></OL>
<P>下面我们就按照这个步骤来一步步讨论SOAP客户端开发种种问题。在本案例中，和服务端进行交互的客户端是通过一个叫JAXMDelegate的类来进行的。GUI客户程序通过调用JAXMDelegate来获得查询结果（具体的数据传输机制和设计模式见本系列第二篇文章）。首先我们来看怎么创建SOAP连接。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=6><SPAN class=atitle><FONT face=Arial size=4>创建 SOAP 连接</FONT></SPAN></A></P>
<P><BR>由于我们使用的是点对点的消息发送模型，所以连接的类型是SOAPConnection。如例程10所示。 </P>
<P>例程10 创建连接</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;
import javax.xml.soap.*;
…
public class JAXMDelegate implements BookBusiness
{
 SOAPConnection con =null;
 EndpointLocator locator=new EndpointLocator();
 Collection allbook;//cache 
 public JAXMDelegate()
 {
  allbook=new ArrayList();
  try
  {
   SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
              con = scf.createConnection();
        }
        catch(Exception e)
        {
         e.printStackTrace();
        }
    }
…
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在上面的例子中，SOAPConnection是通过SOAPConnectionFactory来创建的。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=7><SPAN class=atitle><FONT face=Arial size=4>创建 SOAP 消息</FONT></SPAN></A></P>
<P><BR>接下来是创建消息，在本案例中，有一个专门的方法来创建消息，如11所示。 </P>
<P>例程11 创建消息</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">…
//target为目标名字空间，name为查询某本特定书的书字，category为要查询的类别
public SOAPMessage createMessage(String target,String name,String category) 
    {
     try
     {
          MessageFactory mf = MessageFactory.newInstance();
          SOAPMessage msg = mf.createMessage(); 
       SOAPPart sp = msg.getSOAPPart();
   
   SOAPEnvelope envelope = sp.getEnvelope();   
   //SOAPHeader hdr = envelope.createSOAPHeader();
   
   SOAPBody body = envelope.getBody();
   // AttachmentPart attachment = msg.createAttachmentPart();
msg.saveChanges();
    …
  }
  catch(Exception e)
  {
   e.printStackTrace();
   return null;
  }  
 }
…
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>可以看出创建消息的步骤，首先通过MessageFactory来创建一个消息实例，然后依次创建SOAPEnvelope、SOAPHeader、SOAPBody、AttachmentPart，最后调用msg.saveChanges()来保存消息的变化。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=8><SPAN class=atitle><FONT face=Arial size=4>在SOAP消息里增加数据</FONT></SPAN></A></P>
<P><BR>在SOAP消息里增加数据如例程12所示。 </P>
<P>例程12 往SOAP消息里增加数据</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">//创建名字空间
Name bodyName=envelope.createName("books",target,"http://hellking.webservice.com");
   //增加body元素
SOAPBodyElement gpp=body.addBodyElement(bodyName);
   
   if(category!=null)
   {
    //增加category
gpp.addChildElement("category").addTextNode(category);
   }
   
   if(name!=null)
   {
    gpp.addChildElement("name").addTextNode(name);
   } 
   msg.saveChanges();
   return msg;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>如果例程11中传入的category参数不为空，那么创建好的消息结构应该如例程13所示。</P>
<P>例程13 客户端创建的消息的结构</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
 &lt;soapenv:Body&gt;
  &lt;GetBookByCategory:books xmlns:GetBookByCategory="http://hellking.webservice.com"&gt;
   &lt;GetBookByCategory:category&gt;computer&lt;/GetBookByCategory:category&gt;
  &lt;/GetBookByCategory:books&gt;
 &lt;/soapenv:Body&gt;
&lt;/soapenv:Envelope&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>注意上面的computer为客户端要查询的参数。如果觉得上面填充消息的过程过于复杂，我们也可以从文件填充消息内容，如例程14所示。</P>
<P>例程14 从文件填充消息内容</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">import javax.xml.soap.SOAPElement;
import java.io.FileInputStream;
import javax.xml.transform.stream.StreamSource;
…
StreamSource preppedMsgSrc = new StreamSource( 
                 new FileInputStream("e://msgs//book_getBycategory.msg"));
        soapPart.setContent(preppedMsgSrc);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>可以看出，从文件里填充消息比较快速。这两种方法各有所长，因为如果客户端的请求的种类特别多的话，特别是请求带有参数的话，那么我们要为每一种请求都预先写一个SOAP消息文件，可能是不符合实际的；但是如果客户端请求的类型比较固定，那么事先编写好SOAP消息然后再调用不失是一种好的选择。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=9><SPAN class=atitle><FONT face=Arial size=4>发送消息</FONT></SPAN></A></P>
<P><BR>发送消息相对比较简单，首先要获得一个Endpoint，这个Endpoint就是要发送的消息的目标（也就是接收消息Servlet的url），然后就发送消息，如例程15所示。 </P>
<P>例程15 发送SOAP消息</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">EndpointLocator locator=new EndpointLocator();
…
try
{
 SOAPMessage msg=createMessage("GetBookByCategory",null,category); 
     String endpoint=locator.getBookByCategory_Endpoint();
 SOAPMessage reply=con.call(msg , new URL(endpoint));
    …
}
…
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>EndpointLocator是终端定位器，调用getBookByCategory_Endpoint()它返回的结果将是"http://localhost:8080/jaxm_jaxrpc/listbycategory"，这个地址就是ListByCategory Servlet的RUL，更据不同的设置来确定。由于使用的是点对点的消息发送模型，调用con.call()后返回的也是SOAPMessage。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=10><SPAN class=atitle><FONT face=Arial size=4>对SOAP应答进行处理</FONT></SPAN></A></P>
<P><BR>接下来对消息进行处理，因为不管查询的结果如何，返回的消息都是例程2所示的结构，故我们使用一个专门的类来把SOAP消息转化成包含有BookVO的Collection，这个类是SOAPToBeanEngine，SOAPToBeanEngine的部分代码如例程16所示。 </P>
<P>例程16 SOAPToBeanEngine的部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">package com.hellking.webservice;
import javax.xml.messaging.*;
…
public class SOAPToBeanEngine implements DTOEngine
{
 SOAPMessage reply;
 Collection bookVos;//转换后的结果，用Collection表示
     //构造方法，reply为要转换的SOAP消息
 public SOAPToBeanEngine(SOAPMessage reply)
 {
  this.reply=reply;
 }
 …
 public Collection getResult()
 {
  build();
  return bookVos;
 } 
    //build为具体转换的方法    
    public void build()
    {
     try
     {
      
      Collection ret=new ArrayList();
      //System.out.println(reply.getSOAPPart().getEnvelope().getBody().getElementName());
      Iterator child=reply.getSOAPPart().getEnvelope().getBody().getChildElements();①
      SOAPElement bookall=(SOAPElement)child.next();②
      Iterator books=bookall.getChildElements();③
      SOAPEnvelope env=reply.getSOAPPart().getEnvelope();
      
      SOAPElement temps;      
      Name name;
      
      while(books.hasNext())
      {
       
       BookVO bookVo=new BookVO();
       temps=(SOAPElement)books.next();
       String id=(String)(
        (temps.getAttributeValue(
         (Name)temps.getAllAttributes().next()
                             )
        ));
       bookVo.setIsbn(id);④
       name=env.createName("name","books","http://hellking.webservice.com" );⑤
       bookVo.setName(
        ((SOAPElement)temps.getChildElements(name).next())
        .getValue());
       //System.out.println(bookVo.getName());
       
       name=env.createName("price" ,"books", "http://hellking.webservice.com" );
       bookVo.setPrice(Float.parseFloat(
        ((SOAPElement)temps.getChildElements(name).next())
        .getValue()));
       
       name=env.createName("category", "books", "http://hellking.webservice.com"  );
       bookVo.setCategory(
        ((SOAPElement)temps.getChildElements(name).next())
        .getValue());
        
       name=env.createName("publisher","books","http://hellking.webservice.com"  );
       bookVo.setPublisher(
        ((SOAPElement)temps.getChildElements(name).next())
        .getValue());
       
       name=env.createName("description","books","http://hellking.webservice.com"  );
       bookVo.setDescription(
        ((SOAPElement)temps.getChildElements(name).next())
        .getValue());
       // bookVo.setDescription("kdjfkdjfj");
        
       name=env.createName("author","books","http://hellking.webservice.com"  );
       Collection au=new ArrayList();
       Iterator auth=temps.getChildElements(name);
       while(auth.hasNext())
       {
        
        SOAPElement tempp=(SOAPElement)auth.next();
        
        //System.out.println("author:"+tempp.getValue());
        au.add(tempp.getValue());
       }       
       bookVo.setAuthors(au);
       ret.add(bookVo);       
      }
      bookVos=ret;       
     }
     catch(Exception ex)
     {
       ex.printStackTrace();   
   //错误处理…   
     }
  }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>注意上面例子中的①之类的标号，对应如图6所示的元素。</P><BR><A name=N102AA><B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/image006.gif"> <BR>
<P>图6 SOAP消息</P>
<P>如上所示，在开发中，为了减少层之间的耦合性，我们一般不把SOAP消息直接发送到GUI客户端，而是先处理，把它转换成Java的基本数据类型或者Collection等类型。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=11><SPAN class=atitle><FONT face=Arial size=4>GUI客户端</FONT></SPAN></A></P>
<P><BR>最后我们看一下GUI客户端怎么使用JAXMDelegate来调用业务。 </P>
<P>例程17 BookClientGUI部分代码</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>

<FONT face="Lucida Console">…
public class BookClientGUI 
{
JTable table;//表格
…
int amount;
    Collection books;//表示的数据
     BookBusiness business; 
 JButton search,findAll,findByCategory;
 
 public BookClientGUI()
 {
  business=new JAXMDelegate();//生成一个JAXMDelegate，
  books=business.getAllBooks();//获得所有的图书信息
  //amount=books.size();
  //System.out.println(amount);
 }
 public static void main(String[] args)
 {  
  new BookClientGUI().go();
 }
 public void go()
 {
findByCategory=new JButton("按类别查找");
findByCategory.addMouseListener(new _MouseListener());
…
}
public void showResult()
{
  clearTable();
      Iterator book_i=books.iterator();
    …
}
public void clearTable()
{
…
}
class _MouseListener extends MouseAdapter
{
  public void mouseClicked(MouseEvent e)
  {
 if(((JButton)e.getSource()).getLabel().equals("按类别查找"))
   {
     books=business.getBookByCategory((String)jTextField.getText());
     showResult();
   }
    …
}
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>JAXMDelegate实现了BookBusiness接口，BookClientGUI持有BookBusiness的实例，它通过这个实例来获得信息。BookBusiness返回的信息都是java.util.Collection，这样，给我们编程带来了极大的便利性。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=12><SPAN class=atitle><FONT face=Arial size=4>总结</FONT></SPAN></A></P>
<P><FONT face=Arial size=4></FONT>
<P>本文结合一个具体的案例，介绍了使用JAXM来构造Web服务的方法。需要强调的是，如果使用点对点的消息发送模型，那么服务端Servlet必须实现ReqRespListener接口，onMessage()方法将是开发服务端Servlet的重点任务。客户端编程中，将按照以下步骤进行： </P>
<UL>
<LI>创建 SOAP 连接 
<LI>创建 SOAP 消息 
<LI>在SOAP消息里增加数据 
<LI>发送消息 
<LI>对SOAP应答进行处理 </LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jaxm/part1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=13><SPAN class=atitle><FONT face=Arial size=4>下一步</FONT></SPAN></A></P>
<P><FONT face=Arial size=4></FONT>
<P>经过以上的逐步的解释，相信读者对JAXM编程已经有一个比较深入发了解。您可以在这里。</P>
<P>可以看出，通过使用一定的设计模式和接口，我们可以减少各层之间的耦合，在下一篇中，我将继续深入分析JAXM设计的体系结构和模式。 </P></B></B></B></B></B></B></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/Victor/aggbug/24238.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-16 16:12 <a href="http://www.blogjava.net/Victor/articles/24238.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用JAXM发送和接收SOAP消息</title><link>http://www.blogjava.net/Victor/articles/23994.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Thu, 15 Dec 2005 03:25:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23994.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23994.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23994.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23994.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23994.html</trackback:ping><description><![CDATA[<SPAN class=p11b>在本篇技巧文章中，作者兼开发人员 Nicholas Chase 向您演示如何使用用于XML消息传递的 Java API（Java API for XML Messaging (JAXM)）简化创建和发送 SOAP 消息的过程。<BR><BR>Web 服务的基础在于以标准格式发送和接收消息以便使所有系统都能理解。通常，那种格式是简单对象访问协议（Simple Object Access Protocol (SOAP)）。SOAP 消息可以手工生成和发送，但是用于 XML 消息传递的 Java API（JAXM）使许多必需步骤（如创建连接或创建并发送实际消息）自动化。这篇技巧文章记录了一个同步 SOAP 消息的创建和发送。<BR><BR>这个过程包括创建 SOAP 连接、创建 SOAP 消息、填充消息、发送消息、检索应答。<BR><BR>JAXM 可以作为 Java XML Pack（2002 年春季版）的一部分和 Java Web Services Developer Pack EA2（请参阅参考资料）的一部分而获得。后者还包含了一份 Tomcat Web 服务器以及样本应用程序的副本。那些样本 Web 服务之一作为本技巧文章中 SOAP 消息的目的地，这个例子中实际服务的内容和功能却不是很重要。<BR><BR>
<CENTER><FONT color=#000099><STRONG>SOAP 消息结构</STRONG></FONT></CENTER><BR><BR>一个基本的SOAP消息由包含两个主要部分（报头和主体）的封套组成。应用程序决定如何使用这些部分，但整个消息必须遵循特定的 XML 结构，例如：<BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code bgColor=#e6e6e6><PRE><CCID_CODE>
样本 SOAP 消息
&lt;soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"&gt;
    &lt;soap-env:Header/&gt;
    &lt;soap-env:Body&gt;
        &lt;cal:schedule xmlns:cal="http://www.example.com/calendar"&gt;
            &lt;cal:newitem&gt;
                &lt;cal:eventDate&gt;4/10/2002&lt;/cal:eventDate&gt;
                &lt;cal:title&gt;Fun With Frogs&lt;/cal:title&gt;
            &lt;/cal:newitem&gt;
        &lt;/cal:schedule&gt;
    &lt;/soap-env:Body&gt;
&lt;/soap-env:Envelope&gt;</CCID_CODE>
</PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在这个例子中，报头为空，而主体包含目的地为一个日历应用程序的信息。<BR><BR>请注意这个消息的结构。Envelope 包含 Header 和 Body 元素，而三者全都是 http://schemas.xmlsoap.org/soap/envelope/ 名称空间的一部分。整个消息将通过一个 SOAP 连接发送到一个 Web 服务中。<BR><BR>
<CENTER><FONT color=#000099><STRONG>创建连接和消息</STRONG></FONT></CENTER><BR><BR>第一步是创建整个类和连接：<BR><BR><B>创建连接</B><BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code bgColor=#e6e6e6><PRE><CCID_CODE>
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConnection;
public class SOAPTip {
   public static void main(String args[]) {
      try {
         //First create the connection
         SOAPConnectionFactory soapConnFactory = 
                            SOAPConnectionFactory.newInstance();
         SOAPConnection connection = 
                             soapConnFactory.createConnection();
         //Close the connection            
         connection.close();
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}</CCID_CODE>
</PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>SOAP消息可以通过使用SOAPConnection直接发送，或使用消息传递提供程序间接发送。在这个例子中，应用程序通过使用工厂（factory）创建 SOAPConnection 对象。<BR><BR>工厂也创建消息本身：<BR><BR><B>创建消息对象</B><BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code bgColor=#e6e6e6><PRE><CCID_CODE>
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPBody;
public class SOAPTip {
   public static void main(String args[]) {
      try {
         //First create the connection
         SOAPConnectionFactory soapConnFactory = 
                            SOAPConnectionFactory.newInstance();
         SOAPConnection connection = 
                             soapConnFactory.createConnection();
         //Next, create the actual message
         MessageFactory messageFactory = MessageFactory.newInstance();
         SOAPMessage message = messageFactory.createMessage();
         //Create objects for the message parts            
         SOAPPart soapPart =     message.getSOAPPart();
         SOAPEnvelope envelope = soapPart.getEnvelope();
         SOAPBody body =         envelope.getBody();
         //Close the connection            
         connection.close();
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}</CCID_CODE>
</PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>首先，通过使用 MessageFactory 来创建消息本身。这个消息已包含如 envelope 和 header 等基本部分的空白版本。SOAPPart 包含 envelope，而 envelope 包含主体。同时创建所需对象（如 SOAPBody）的引用。<BR><BR>接着，填充 SOAPBody：<BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code bgColor=#e6e6e6><PRE><CCID_CODE>
填充主体
...
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
public class SOAPTip {
   public static void main(String args[]) {
      try {
...
         //Create objects for the message parts            
         SOAPPart soapPart =     message.getSOAPPart();
         SOAPEnvelope envelope = soapPart.getEnvelope();
         SOAPBody body =         envelope.getBody();
        //Populate the body
        //Create the main element and namespace
        SOAPElement bodyElement = 
                  body.addChildElement(envelope.createName("schedule" , 
                                                                "cal", 
                                    "http://www.example.com/calendar"));
        //Add content
        bodyElement.addChildElement("cal:newitem").addTextNode("contentHere");
        //Save the message
        message.saveChanges();
        //Check the input
        System.out.println("\nREQUEST:\n");
        message.writeTo(System.out);
        System.out.println();
         //Close the connection            
         connection.close();
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}</CCID_CODE>
</PRE></TD></TR></TBODY></TABLE></SPAN><!--end_1--><SPAN class=p11b><BR><BR>SOAP 消息的主体就象任何其它 XML 元素，您可以在其中添加子元素（如 schedule 元素）。通常，您可以使用 addChildElement(elementname)，但是这里演示的 envelope.createName() 方法使用用于数据或有效负载的名称空间声明简化了元素的创建。的确，创建 schedule 元素从而创建了 bodyElement SOAPElement 对象。然后，bodyElement 对象可以给其自己的子元素 cal:newitem 添加其自己的文本节点。通过这种方式，您可以象构建任何其它 XML 文档一样构建 XML 结构。<BR><BR>然而，使用 JAXM，您也有机会通过使用外部文件直接创建消息的 SOAPPart。例如，第一个清单中的 XML 结构保存在文件 prepped.msg 中，而且可以调用它来替代手工构建文档。<BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code bgColor=#e6e6e6><PRE><CCID_CODE>
从外部文件创建消息
...
import javax.xml.soap.SOAPElement;
import java.io.FileInputStream;
import javax.xml.transform.stream.StreamSource;
public class SOAPTip {
   public static void main(String args[]) {
...
         //Create objects for the message parts            
         SOAPPart soapPart =     message.getSOAPPart();
         SOAPEnvelope envelope = soapPart.getEnvelope();
         SOAPBody body =         envelope.getBody();
        //Populate the Message
        StreamSource preppedMsgSrc = new StreamSource( 
                 new FileInputStream("prepped.msg"));
        soapPart.setContent(preppedMsgSrc);
         //Save the message
         message.saveChanges();
...
    }
}</CCID_CODE>
</PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>结果就是准备发送的 SOAP 消息。<BR><BR><B>发送消息</B><BR><BR>对于同步消息，发送 SOAP 消息和接收应答是在单个步骤中发生的：<BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code bgColor=#e6e6e6><PRE><CCID_CODE>
发送消息
...
import javax.xml.messaging.URLEndpoint;
public class SOAPTip {
   public static void main(String args[]) {
...
         //Check the input
         System.out.println("\nREQUEST:\n");
         message.writeTo(System.out);
         System.out.println();
        //Send the message and get a reply   
        //Set the destination
        URLEndpoint destination = 
              new URLEndpoint("http://localhost:8080/jaxm-simple/receiver");
        //Send the message
        SOAPMessage reply = connection.call(message, destination);
         //Close the connection            
         connection.close();         
...
    }
}</CCID_CODE>
</PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>实际的消息是使用 call() 方法发送的，该方法把消息本身和目的地作为参数，然后返回第二个 SOAPMessage 作为应答。目的地必须是一个 Endpoint 对象，或者是这个例子中的 URLEndpoint。这个示例使用来自 JWSDP 的一个样本 servlet，它只用于获取响应。<BR><BR>call() 方法一直处于阻塞状态，直到它接收到返回的 SOAPMessage 为止。<BR><BR>
<CENTER><FONT color=#000099><STRONG>响应</STRONG></FONT></CENTER><BR><BR>返回的 SOAPMessage — reply — 是 SOAP 消息，它与已发送的消息格式相同，因此可以象操作任何其它 XML 消息那样操作它。SOAP 允许您通过使用 XSLT 直接转换应答：<BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code bgColor=#e6e6e6><PRE><CCID_CODE>
读取响应
...
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
public class SOAPTip {
   public static void main(String args[]) {
      try {
...
         //Send the message
         SOAPMessage reply = connection.call(message, destination);
        //Check the output
        System.out.println("\nRESPONSE:\n");
        //Create the transformer
        TransformerFactory transformerFactory = 
                           TransformerFactory.newInstance();
        Transformer transformer = 
                        transformerFactory.newTransformer();
        //Extract the content of the reply
        Source sourceContent = reply.getSOAPPart().getContent();
        //Set the output for the transformation
        StreamResult result = new StreamResult(System.out);
        transformer.transform(sourceContent, result);
        System.out.println();
         //Close the connection            
         connection.close();
...            
     }
}</CCID_CODE>
</PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>象在任何 XSLT 应用程序中那样创建 Transformer 对象。在这个例子中，我们只希望输出内容，所以没有用到样式表。这里，内容本身就是消息的整个 SOAP 部分（与可能包含附件的 SOAP 消息本身不同）。您还可以在处理之前抽取封套和主体。这个例子中的结果只是 System.out，但它可以是通常用于转换的任何选择。照常进行转换。<BR><BR>
<CENTER><FONT color=#000099><STRONG>下一步</STRONG></FONT></CENTER><BR><BR>虽然本示例中的端点是提供静态响应的 servlet，但是实际的响应取决于服务的功能和请求的性质。同时，虽然本篇技巧文章演示了消息的同步发送和接收，但是通过使用 ProviderConnection 对象而不是 SOAPConnection，JAXM 允许使用消息传递提供程序进行异步发送。该提供程序一直保存这个消息，直到成功发送消息为止。<BR><BR>JAXM 还允许使用 profile，这样很容易创建诸如 SOAP-RP 或 ebXML 消息那样的专门 SOAP 消息，而且还能使非 XML 附件能够附加到 SOAP 消息中。<BR><BR><B>参考资料</B><BR><BR>请查看 W3C 中的各种与 Web 服务相关的建议书的情况。<BR><BR>JAXM 可以作为 Java XML Pack（2002 年春季版）的一部分和 Java Web Services Developer Pack EA2 的一部分而获得。<BR><BR>IBM WebSphere Studio Application Developer 是用于构建、测试和部署 Web 服务的易用的集成开发环境。<BR><BR>要获取完整的 Web 服务工具箱，请下载 IBM 的 Web Services Development Kit。<BR><BR>在 developerWorks 的 XML 和 Web 服务专区查找更多参考资料。<BR><BR><B>关于作者</B><BR><BR>Nicholas Chase 一直在参与如 Lucent Technologies、Sun Microsystems、Oracle 和 Tampa Bay Buccaneers 等公司的网站开发。Nick 曾经是一位高中物理教师、低级放射性废物设备的管理员、在线科幻小说杂志的编辑、多媒体工程师和 Oracle 讲师。近来，他是佛罗里达州克利尔沃特 Site Dynamics Interactive Communications 的首席技术官，而且是有关 Web 开发的三本书，包括 Java and XML From Scratch（Que）和即将出版的 Primer Plus XML Programming（Sams）的作者。他愿意倾听读者的意见，可以通过 nicholas@nicholaschase.com 与他联系。<BR></SPAN><img src ="http://www.blogjava.net/Victor/aggbug/23994.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-15 11:25 <a href="http://www.blogjava.net/Victor/articles/23994.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SOAP Header:扩展SOAP能力的途径</title><link>http://www.blogjava.net/Victor/articles/23760.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Wed, 14 Dec 2005 01:19:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23760.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23760.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23760.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23760.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=5><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=5 border=0></TD>
<TD width="100%">
<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR>
<TD width=8><IMG height=21 alt="" src="http://www.ibm.com/i/c.gif" width=5></TD>
<TD width=160>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/xml/x-soapheader/#1">更新的SOAP概念</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/xml/x-soapheader/#2">SOAP角色与SOAP Header处理模式</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/xml/x-soapheader/#3">SOAP功能扩展: 权限认证</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/xml/x-soapheader/#4">SOAP功能扩展: 事务控制</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/xml/x-soapheader/#5">小结</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/xml/x-soapheader/#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/xml/x-soapheader/#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/xml/x-soapheader/#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/xml/soap/index2.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP的消息结构与数据的组织方法</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>订阅:</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=1 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/newsletter/">developerWorks 时事通讯</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=150 bgColor=#000000 colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/xml/x-soapheader/#author1"><NAME>柴晓路</NAME></A><BR>Chief System Architect<BR>2001 年 10 月 </P>
<BLOCKQUOTE>本文应SOAP/1.2规范推出的技术背景，就运用SOAP Header扩展SOAP的功能展开讨论。当具体的应用中运用了一些与应用本身关联不是太大而更面向底层控制的服务的时候应当采用SOAP Header来传输这些控制信息，理由是这些服务往往是平台的功能而非具体应用所要实现的功能。从体系架构的观点来看，解析SOAP Header的就可以由平台模块来完成，通过插入不同的标准化的SOAP Header条目解析模块来完成不同目的的控制功能。而相应的，解析SOAP Body是由应用模块来完成。这样在开发和部署上将会非常地清晰。</BLOCKQUOTE>
<P>W3C XML Protocol工作组在今年7月发布了SOAP Version 1.2 Working Draft (SOAP规范1.2版草案，网址是" http://www.w3.org/TR/2001/WD-soap12-20010709/")。同时在2001年4月，在美国的San Jose召开的Web服务研讨会上正式确立了SOAP作为Web服务的核心规范的地位。</P>
<P>在SOAP/1.2版中，对于如何拓展SOAP的能力作了明确的指示性的描述，那就是SOAP Body关注于调用本身(基本没有变化)，而SOAP Header从先前的可以由SOAP中介结点处理的模糊指示转变为SOAP Header是扩展SOAP功能的最佳途径的明确性指示。</P>
<P>我们知道，SOAP的应用已经有了一定的阶段，各种基于SOAP调用的Web服务纷纷出现。然而，目前的应用模式基本上停留在远程过程/对象的调用上，基于多次协调调用或者遵循上下文的调用模式尚很少使用，这其实是受简单的SOAP消息的制约。如果扩展，而且是遵循标准的扩展SOAP消息以满足更复杂情况下的应用成为了目前的一个发展趋势。</P>
<P><A name=1><SPAN class=atitle2>更新的SOAP概念</SPAN></A><BR></P>
<P>在讨论运用SOAP Header来扩展SOAP功能之前，我们先来看看SOAP/1.2中更新的一些SOAP的基本概念，这些是理解后面的内容的基础。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP结点 (SOAP Node)</B> </P>
<P>SOAP结点根据SOAP定义的整套规范来处理SOAP消息。SOAP结点有责任遵守SOAP消息交换的规则以及提供通过依赖底层协议的SOAP绑定来访问的服务。任何不符合SOAP约定的情况都将导致SOAP结点产生一个SOAP fault(SOAP错误)。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP条目 (SOAP Block)</B> </P>
<P>SOAP条目是一个句法上的结构，它用于包含一个逻辑上的单一元素，这一元素是需要被SOAP结点处理的。一个SOAP条目是由该条目最外层元素的完整修饰名(带命名空间修饰)所标识的，这个完整修饰名是由一个局部名和一个命名空间URI组成的。封装在SOAP Header中的SOAP条目称为Header条目，而封装在SOAP body中的SOAP条目为Body条目。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP Header (SOAP头)</B> </P>
<P>能够被SOAP消息传输路径中任意的SOAP接受者结点处理的一组SOAP条目(0个或多个)。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP Body (SOAP体)</B> </P>
<P>能够被SOAP消息路径中的最终SOAP接受结点处理的一组SOAP条目(0个或多个)。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP发送者</B> </P>
<P>SOAP发送者是发出SOAP消息的SOAP结点。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP接收者</B> </P>
<P>SOAP接收者是接受SOAP消息的SOAP结点。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP消息路径</B> </P>
<P>为传送一个简单的SOAP消息而要经过的一组SOAP发送者和SOAP接受者。其中包含了初始SOAP发送者、零个或多个SOAP中介结点以及最终的SOAP接受者。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">初始SOAP发送者</B> </P>
<P>SOAP消息的最初产生者，同时也是SOAP消息路径的第一个结点。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">SOAP中介结点</B> </P>
<P>SOAP中介结点即是SOAP接收者也是SOAP发送者，是SOAP消息可到达的某一个应用程序。当SOAP消息沿着SOAP消息路径传输时，SOAP中介结点将处理一组确定的SOAP条目，然后它将消息转发给消息路径的下一个SOAP结点，直至传送到最终SOAP接收者。</P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">最终SOAP接收者</B> </P>
<P>由初始SOAP发送者指定的通过SOAP消息路径传送的SOAP消息的最终的接收者。如果在SOAP消息路径中有SOAP结点产生了SOAP错误，那么SOAP消息将不会到达最终接收者。</P>
<P>同时SOAP/1.2使用了新的命名空间，命名空间前缀"env"和"enc"等关联的SOAP/1.2命名空间分别位于："http://www.w3.org/2001/06/soap-envelope"和"http://www.w3.org/2001/06/soap-encoding"。</P>
<P><A name=2><SPAN class=atitle2>SOAP角色与SOAP Header处理模式</SPAN></A><BR></P>
<P>当SOAP结点接受并处理一个SOAP消息的时候，该SOAP结点将被告知应当以一个或多个SOAP处理角色来处理，这些SOAP角色是由SOAP角色名来标识，SOAP角色名的具体表示是使用一个env:actor属性来表示，其值是一个URI。</P>
<P>每个SOAP结点都必须以一个指定的角色来处理，也就是说任意一个SOAP结点都属于这个角色，这个角色使用命名为"http://www.w3.org/2001/06/soap-envelope/actor/next"的SOAP角色来表示，同时可以按照需要应用零个或多个其他的SOAP额外角色，当然这些角色应当使用不同与前面介绍的这个SOAP角色名。</P>
<P>SOAP结点可以通过以匿名SOAP角色来实施处理以使得自己成为最终SOAP接收者。当SOAP结点在处理一个SOAP消息的时候，其表现出的SOAP角色在整个处理过程中不得更改。这是因为SOAP规范只涉及如何处理单个SOAP消息而无需考虑状态(这也是SOAP设计目标之一的简明性的体现)，因此是否允许在处理单个SOAP消息的时候转换角色是没有意义的。</P>
<P>从本质上说，SOAP角色名是用来识别SOAP结点的，通常使用某种URI的形式，SOAP的角色名并没有与路由或者消息交换的语义相联系。举例来说，一个SOAP角色可以被命名为一个用于在发送SOAP消息给适当SOAP结点中表示接收结点访问入口的URI。相反，也有这样一些SOAP角色的名字，这些名字或者直接和消息路由相联系(例如，"http://example.org/banking/anyAccountMgr")，或者和路由没有联系(例如，当一个消息头被用来携带这样一种指示性的信息，该指示信息用于告知任何相关的SOAP消息的接受应用软件，这个SOAP消息是长期不变的，因此是能够被安全的缓存和重用的，在这种SOAP消息头中，可以利用一个标识"所有缓存管理软件"的URI来指明SOAP角色)，通过名字使用这些SOAP角色也是合适的。</P>
<P>总而言之，SOAP角色的名并没有预定义为一定要与某种语义相关联，用户可以使用某种语义关联的URI来表示，也完全可以用类似UUID这样的没有语义的URI来表示，这完全要看具体的应用的需要。</P>
<P>SOAP Header条目包含可选的env:actor属性，用来把他们定位到合适的SOAP结点。没有该属性的SOAP Header隐含地被定位到一个匿名的SOAP角色，这意味着他们将被最终SOAP接收者所处理。我们把SOAP actor属性的值(隐含的或者直接指明的)作为相应SOAP条目(SOAP Header条目或者SOAP Body条目)的SOAP角色。</P>
<P>如果是：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>SOAP条目中SOAP actor属性的值(如果出现的话)匹配了一个SOAP结点的角色； 
<LI>或者是当SOAP条目没有actor属性(不仅对于SOAP Header条目有效，也同时包括SOAP Body条目，值得注意的是在SOAP/1.1中，actor属性只能应用于SOAP Header条目)，而该SOAP结点已经被假设为匿名SOAP角色。 </LI></UL>
<P>这时我们就说SOAP条目被指向一个SOAP结点，同时将被该SOAP结点处理。</P>
<P><A name=N100F0><B>Figure 1. SOAP Header条目的标准化处理模式 </B></A><BR><IMG height=322 alt="" src="http://www-128.ibm.com/developerworks/cn/xml/x-soapheader/soap-10.gif" width=480 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P>我们认为随着时间的过去，会有大量的SOAP Header函数规范出现，而且每个SOAP结点都可以包含一个或多个处理这些扩展所必须的软件。如果SOAP结点的应用软件是完全兼容而且实现了那些由条目中完整修饰的最外层元素名所传递的语义，我们说这个SOAP Header被一个SOAP结点理解。</P>
<P>在Figure 1中，以形象化的形式描述了这种将来可能的依据某种Header函数规范的标准化处理方式。其实质就是在SOAP的框架下，定义了一整套SOAP Header条目的语义集及其处理规范。例如，将来可能会出现一组专用于访问控制的SOAP Header条目集。其中可能包含这样两种SOAP Header条目：(假定他们的命名空间为xmlns:auth="soap:header: authentication")</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>用户认证：auth:get_authToken，在这个SOAP Header条目下，包含两个子元素userID和password，用于完成用户认证操作并获取认证令牌； 
<LI>访问授权：auth:judge_accessList，在这个SOAP Header条目下，包含一系列的子元素accessResource，每个accessResource有两个属性authToken和resourceURI，分别用于表示提供的认证令牌和待访问的资源URI，这个SOAP Header条目用于判定这些资源是否授权于给定的人证令牌以访问权限，如果是，那么授予了什么样的权限。 </LI></UL>
<P>如果这样一种SOAP Header条目的规范被投入实用后，任何SOAP结点只需要部署了兼容该规范的SOAP处理程序，就能够处理这样的SOAP Header条目。</P>
<P>对于SOAP结点而言，除env:actor属性之外，尚有另一个重要的属性：env:mustUnderstand。当定位到一个SOAP结点的SOAP Header条目的env:mustUnderstand属性为"1"，被指向的SOAP结点必须：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>或者依照由条目中完整修饰的最外层元素名传递的语义来处理SOAP块； 
<LI>或者更本不处理SOAP消息而失败。 </LI></UL>
<P>也就是说，不可以在任何情况下忽略对这种SOAP Header条目的处理。</P>
<P><A name=3><SPAN class=atitle2>SOAP功能扩展: 权限认证</SPAN></A><BR></P>
<P>在以下的篇幅，将结合具体的应用实例来详细地阐述SOAP Header条目的意义以及SOAP Header属性的作用，同时期望大家能够了解到从设计者的角度，是如何对SOAP进行体系架构的。</P>
<P>第一个例子是利用SOAP Header条目进行权限认证:</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>&lt;env:Header xmlns:env="http://www.w3.org/2001/06/soap-envelope" &gt;
  &lt;auth:authentication xmlns:auth="http://example.org/authentication "
                    env:actor="authentication:signin_service"
                    env:mustUnderstand="1"&gt;
    &lt;auth:userID&gt;testuserid&lt;/auth:userID&gt;
    &lt;auth:password&gt;[encodedPassword]&lt;/auth:password&gt;
    &lt;auth:redirection&gt;http://example.com/service/&lt;/auth:redirection&gt;
  &lt;/auth:authentication&gt;
&lt;/env:Header&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在这个例子中，SOAP Header条目authentication被交付给专门的权限认证Web服务进行用户认证(该服务使用角色名"authentication:signin_service"来标识)，该Web服务通过检查authentication条目中包含的用户名(userID)和密码(password)来确认该用户是否能通过认证检查。如果无法通过认证检查，将返回调用者一个SOAP错误，如果能够通过认证检查，则该认证Web服务在该Header条目中删除userID和password，然后插入一个新的元素authInfo，这是一个认证令牌，可用于以后在需授权服务调用中使用。接着，该Web服务将这条消息传递给由redirection元素指定的地址的Web服务，这个Web服务可以通过校验认证令牌以审核该次调用。</P>
<P>从认证Web服务发送到后续Web服务的SOAP消息中的Header片断的可能内容可参见下面的代码段：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>&lt;env:Header xmlns:env="http://www.w3.org/2001/06/soap-envelope" &gt;
  &lt;auth:authentication xmlns:auth="http://example.org/authentication "
                    env:mustUnderstand="1" &gt;
    &lt;auth:authInfo&gt;[encodedAuthInfo]&lt;/auth:authInfo&gt;
  &lt;/auth:authentication&gt;
&lt;/env:Header&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>大家可以发现在这个后续的SOAP消息中，authentication条目中的redirection元素也已经被删除了(也就是已经被使用了，作为新的SOAP结点的地址)。</P>
<P><A name=4><SPAN class=atitle2>SOAP功能扩展: 事务控制</SPAN></A><BR></P>
<P>第二个例子是利用SOAP Header条目进行事务控制。这个应用背景是这样的，在一个商务事务处理应用环境中，由Web服务A发起事务，一个事务会包含多个操作，而这些操作可能要经过其他的一些Web服务进行计算后才能生成，而所有的操作将会被发往Web服务Z，由Web服务Z完成整个事务。整个事务的执行模式可参见下图。</P>
<P><A name=N1014A><B>Figure 2. 事务控制模型 </B></A><BR><IMG height=351 alt="" src="http://www-128.ibm.com/developerworks/cn/xml/x-soapheader/soap-11.gif" width=450 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P>其中的消息序列将是这样：</P>
<OL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>Web服务A向Web服务Z发出事务启动的总控消息； 
<LI>Web服务A向Web服务G、H、I发出操作生成请求消息； 
<LI>Web服务G、H、I分别向Web服务Z发出事务中的具体操作的描述消息，以最终完成整个事务。 </LI></OL>
<P>也就是说Web服务A是事务的控制点，Web服务Z是事务的提交点，而Web服务G、H、I则分别是事务的产生点。</P>
<P>总控消息的Header片断的内容为：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>&lt;env:Header xmlns:env="http://www.w3.org/2001/06/soap-envelope" &gt;
  &lt;transaction:transaction
            xmlns:transaction="http://example.org/transaction"
            env:actor="transaction:submission_point"&gt;
            env:mustUnderstand="1"&gt;
    &lt;transaction:transactionKey&gt;8259bd00-2f9c-4493-a09f-414e3a4559a6
      &lt;/transaction:transactionKey&gt;
    &lt;transaction:operations&gt;
      &lt;transaction:count&gt;3&lt;/transaction:count&gt;
    &lt;/transaction:operations &gt;
  &lt;/transaction:Transaction&gt;
&lt;/env:Header&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在这条消息中，有一个SOAP Header条目transaction，它的子元素transactionKey表示了启动的事务的键值。而另一个子元素operations则包含了一个描述该事务包含的所有操作的数量的子元素count。当Web服务Z接受到这条消息后，将为该事务启动一个消息池，该消息池的标识为transactionKey的值。</P>
<P>然后Web服务A向Web服务G、H、I发出事务启动消息，通知这些服务指定事务可以开始提交操作了。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>&lt;env:Header xmlns:env="http://www.w3.org/2001/06/soap-envelope" &gt;
  &lt;transaction:transaction
            xmlns:transaction="http://example.org/transaction"
            env:actor="transaction:operation_point" &gt;
            env:mustUnderstand="1"&gt;
    &lt;transaction:transactionKey&gt;8259bd00-2f9c-4493-a09f-414e3a4559a6
      &lt;/transaction:transactionKey&gt;
    &lt;transaction:action&gt;start&lt;/transaction:action&gt;
  &lt;/transaction:Transaction&gt;
&lt;/env:Header&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>当Web服务G、H、I分别向Web服务Z发出操作消息(参见图 4 25)时，Web服务Z分别将这些收到的消息放入指定的消息池中，当消息池中的操作消息的数量达到count元素中指定的数量后，Web服务Z将关闭该消息池，按照次序，执行该事务，待执行完毕后，向Web服务A发送一个通知消息。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>&lt;env:Header xmlns:env="http://www.w3.org/2001/06/soap-envelope" &gt;
  &lt;transaction:transaction
            xmlns:transaction="http://example.org/transaction"
            env:actor="transaction:submission_point" &gt;
            env:mustUnderstand="1"&gt;
    &lt;transaction:transactionKey&gt;8259bd00-2f9c-4493-a09f-414e3a4559a6
      &lt;/transaction:transactionKey&gt;
    &lt;transaction:operations&gt;
      &lt;transaction:serialNo&gt;1&lt;/transaction:serialNo&gt;
    &lt;/transaction:operations &gt;
  &lt;/transaction:Transaction&gt;
&lt;/env:Header&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=5><SPAN class=atitle2>小结</SPAN></A><BR></P>
<P>在本文中按照SOAP规范的约定，给出了两个运用SOAP Header条目对SOAP的能力进行扩充的例子。我们认为，当具体的应用中运用了一些与应用本身关联不是太大而更面向底层控制的服务的时候应当采用SOAP Header来传输这些控制信息，理由是这些服务往往是平台的功能而非具体应用所要实现的功能。按照规范的约定，SOAP Body是专用于交换调用的具体信息，而控制信息的交互应当由SOAP Header来完成。</P>
<P>这样，从体系架构的观点来看，解析SOAP Header的就可以由平台模块来完成，通过插入不同的标准化的SOAP Header条目解析模块来完成不同目的的控制功能。而相应的，解析SOAP Body是由应用模块来完成。这样在开发和部署上将会非常地清晰。</P>
<P>随着SOAP Header扩展的普遍应用和标准的形成，将意味着SOAP技术的真正成熟。</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/Victor/aggbug/23760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-14 09:19 <a href="http://www.blogjava.net/Victor/articles/23760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用SOAP开发java web服务--Axis开发方案</title><link>http://www.blogjava.net/Victor/articles/23177.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 09 Dec 2005 09:12:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23177.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23177.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23177.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23177.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23177.html</trackback:ping><description><![CDATA[&nbsp;本文的预定读者首先要对j2ee有所了解，熟悉xml，tomcat等基本内容，本文主要是简单介绍一下web服务的基本内容，怎样在java web开发中构建SOAP服务：<BR>&nbsp;一、SOAP（Simple Object Access Protocol）简单对象访问协议，要了解SOAP，首先就需要了解分布式计算的由来，随着下一代的分布式计算体系web服务的出现，SOAP成为了创建和调用通过网络发布的应用程序的实际通信标准。SOAP类似传统的二进制协议IIOP（CORBA）和JRMP（RMI），但它不采用二进制数据表示法，而是采用使用XML的，基于文本的数据表示法。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过XML表示法，SOAP定义了一种小型有线连接协议和编码格式，以表示数据类型、编程语言和数据库，还可以使用各种Internet标准协议作为其消息传输工具，还可以提供表示RPC和文档驱动的消息交换等通信模型的约定。请注意，W3C正致力于SOAP的研究，<A href="http://www.w3c.org/2000/xp/Group/"><FONT color=#000080>http://www.w3c.org/2000/xp/Group/</FONT></A>&nbsp;，并得到了主流供应商的积极响应，以便对于基于XML的协议相关的重要任务达成共识，并定义其关键要求和使用场景。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOAP1.2的基本规范定义了以下基本内容：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1）用于将XML文档表示为结构化SOAP消息的语法和语义<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）在SOAP消息中表示数据的编码标准<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3）用于交换SOAP消息的通信模型<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4）SOAP传输等底层协议的绑定<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOAP消息主要包括了信封头，消息头，主体，附件几部分<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个简单的SOAP消息表示：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POST&nbsp;&nbsp; /StudentInfo&nbsp;&nbsp; HTTP/1.1<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Host：anthropology.cun.edu<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Content-Type: text/xml;charset="utf-8"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Content-Length: 640<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOAPAction:&nbsp;&nbsp;"GetStudentInfo"<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;SOAP-ENV:Envelop xmlns:SOAP-ENV="<A href="http://www.w3c.org/2001/06/soap-envelope"><FONT color=#000080>http://www.w3c.org/2001/06/soap-envelope</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:xsi="<A href="http://www.w3c.org/2001/XMLSchema-instance"><FONT color=#000080>http://www.w3c.org/2001/XMLSchema-instance</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsd="<A href="http://www.w3c.org/2001/XMLSchema"><FONT color=#000080>http://www.w3c.org/2001/XMLSchema</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOAP-ENV:encodingStyle="http://www.w3c.org/2001/06/soap-encoding"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;SOAP-ENV:Header&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;person:mail xmlns:person="http://www.cun.edu/Header"&gt;xyz@cun.edu<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/SOAP-ENV:Header&gt;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;SOAP-ENV:Body&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;m:GetStudentInfo&nbsp;&nbsp; xmlns:m="http://www.cun.edu/jws.student.studentInfo"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;student_name xsi:type='xsd:string'&gt;<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;Wang wen yin<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/student&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/m:GetStudentInfo&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/SOAP-ENV:Body&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/SOAP-ENV:Envelop&gt;<BR>&nbsp;&nbsp; 以上是1.2版本命名空间，1.1的命名空间 SOAP ENVELOPE：<A href="http://schemas.xmlsoap.org/soap/envelop/"><FONT color=#000080>http://schemas.xmlsoap.org/soap/envelop/</FONT></A> ,SOAP ENCODING: <A href="http://schemas.xmlsoap.org/soap/encoding/"><FONT color=#000080>http://schemas.xmlsoap.org/soap/encoding/</FONT></A>&nbsp;<BR>&nbsp;&nbsp;&nbsp;关于SOAP编码规范请参阅<A href="http://www.w3c.org/TR/xmlschema-2/"><FONT color=#000080>www.w3c.org/TR/xmlschema-2/</FONT></A>&nbsp;定义的编码值，其他的一些规范可以上<A href="http://www.w3c.org/"><FONT color=#000080>www.w3c.org</FONT></A> 上具体查看。<BR>二、以下从实际例子来学习，这里我使用的是Apache的一个子项目Axis的具体例子，便于深入了解soap的运行：<BR>&nbsp;&nbsp;&nbsp; 1）下载Axis的相关内容<A href="http://ws.apache.org/axis/"><FONT color=#000080>http://ws.apache.org/axis/</FONT></A>：<BR>&nbsp;&nbsp;&nbsp;&nbsp;2）建立一个实例程序（遵守j2ee的web程序规范），如（WebServiceTest目录）<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把axis中lib文件夹的内容拷到你的WebServiceTest/WEB-INF/lib下，同时上网下载xerces(下载地点：<A href="http://xml.apache.org/xerces-j/"><FONT color=#000080>http://xml.apache.org/xerces-j/</FONT></A>）解释器的包文件xerces.jar，也拷到WebServiceTest/WEB-INF/lib文件夹下,（若要配置log4j，请把属性文件log4j.properties拷到WebServiceTest/WEB-INF/classes文件夹下)<BR>&nbsp;&nbsp;&nbsp;&nbsp;3)修改应用程序WebServiceTest/WEB-INF中的web.xml文件：主要servlet设置如下<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;TestServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-class&gt;org.apache.axis.transport.http.AxisServlet&lt;/servlet-class&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-mapping&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;TestServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;*.jws&lt;/url-pattern&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-mapping&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-mapping&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;TestServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;/servlet/TestServlet&lt;/url-pattern&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-mapping&gt;<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-mapping&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;TestServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;/services/*&lt;/url-pattern&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-mapping&gt;
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;AdminServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-class&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.apache.axis.transport.http.AdminServlet<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-class&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;load-on-startup&gt;100&lt;/load-on-startup&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet&gt;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-mapping&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;AdminServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;/servlet/AdminServlet&lt;/url-pattern&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-mapping&gt;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mime-mapping&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;extension&gt;wsdl&lt;/extension&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mime-type&gt;text/xml&lt;/mime-type&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mime-mapping&gt;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mime-mapping&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;extension&gt;xsd&lt;/extension&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mime-type&gt;text/xml&lt;/mime-type&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mime-mapping&gt;<BR>&nbsp;&nbsp;&nbsp; 你现在可以在网址里输入<A href="http://localhost/WebServiceTest/servlet/TestServlet"><FONT color=#000080>http://localhost/WebServiceTest/servlet/TestServlet</FONT></A>&nbsp;看到了吗？Axis是使用axis.jar包里的org.apache.axis.transport.http.AxisServlet对应用程序进行处理的，基本配置就讲到这里。<BR>三、接着我们来说Axis中的内核。<BR>1）不使用Tomcat引擎运行Axis。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 先建立一个脚步文件，对环境变量classpath进行设置要把lib下的那些包文件的路径全都包括进去，运行：java&nbsp;&nbsp;org.apache.axis.transport.http.SimpleAxisServer&nbsp; &lt;port&gt;<BR>&nbsp;2）内部服务处理程序是org.apache.axis.providers.java.RPCProvider，标志出服务所需的方法，然后提供从SOAP请求消息组成部分的参数。<BR>&nbsp;3）Axis的应用程序端管理功能：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java&nbsp; org.apache.axis.client.AdminClient&nbsp;就会列出参数，可供你选择。我们的例子是：java&nbsp; org.apache.axis.client.AdminClient&nbsp; -l <A href="http://localhost/WebserviceTest/servlet/TestServlet"><FONT color=#000080>http://localhost/WebserviceTest/servlet/TestServlet</FONT></A>&nbsp; list 就会显示出服务列表，返回的是xml文件<BR>4）wsdl2java应用程序可以把wsdl文件创建基于java的程序，如占位程序等<BR>&nbsp;&nbsp;&nbsp;&nbsp; java&nbsp; org.apache.axis.wsdl.WSDL2java&nbsp; &lt;url&gt;<BR>Axis的基本内容说到这里<BR>四、具体例子<BR>&nbsp;1）编写逻辑程序，简单如：SoapTest.java<BR>&nbsp;&nbsp;&nbsp;&nbsp;public class SoapTest{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public String getStr(String name){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "Hello，"+name;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp; 2)&nbsp;部署服务，编写wsdd文件SoapTest_deploy.wsdd:<BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;deployment name="SimapleTest" xmlns="<A href="http://xml.apache.org/axis/wsdd/"><FONT color=#000080>http://xml.apache.org/axis/wsdd/</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns:java="<A href="http://xml.apache.org/axis/wsdd/providers/java"><FONT color=#000080>http://xml.apache.org/axis/wsdd/providers/java</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsd="<A href="http://www.w3.org/2000/10/XMLSchema"><FONT color=#000080>http://www.w3.org/2000/10/XMLSchema</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsi="<A href="http://www.w3.org/2000/10/XMLSchema-instance"><FONT color=#000080>http://www.w3.org/2000/10/XMLSchema-instance</FONT></A>"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;service name="SoapTest" provider="java:RPC"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;parameter name="className" value="SoapTest"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;parameter name="allowedMethods" value="getStr"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/service&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/deployment&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;其中className参数是你的想部署的类名（全名），allowedMethods是调用的服务的方法，如果有多个方法的话可以用空格分开（如：&nbsp;&lt;parameter name="allowedMethods" value="getStr&nbsp; getMoney"/&gt;),当用*的时候表示全部。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;好了现在准备部署了，确保环境路径classpath设置正确，运行：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java&nbsp; org.apache.axis.client.AdminClient&nbsp; -l&nbsp; <A href="http://localhost/WebserviceTest/servlet/TestServlet"><FONT color=#000080>http://localhost/WebserviceTest/servlet/TestServlet</FONT></A>&nbsp; SoapTest_deploy.wsdd<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（这里不懂的话，请参考以上的说明）&nbsp;<BR>&nbsp;ok，呵呵，至此，我们已经完成了一个web服务的部署：测试<A href="http://localhost/WebServiceTest/servlet/TestServlet"><FONT color=#000080>http://localhost/WebServiceTest/servlet/TestServlet</FONT></A>&nbsp; 看里面是否多了一个选择SoapTest服务？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果不想要服务了那重新编写一个wsdd文件，内容改为：<BR>&lt;deployment name="SimapleTest" xmlns="<A href="http://xml.apache.org/axis/wsdd/"><FONT color=#000080>http://xml.apache.org/axis/wsdd/</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns:java="<A href="http://xml.apache.org/axis/wsdd/providers/java"><FONT color=#000080>http://xml.apache.org/axis/wsdd/providers/java</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsd="<A href="http://www.w3.org/2000/10/XMLSchema"><FONT color=#000080>http://www.w3.org/2000/10/XMLSchema</FONT></A>"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns:xsi="<A href="http://www.w3.org/2000/10/XMLSchema-instance"><FONT color=#000080>http://www.w3.org/2000/10/XMLSchema-instance</FONT></A>"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;service name="SoapTest"/&gt;<BR>&lt;/deployment&gt;<BR>和上面一样，对比一下就ok了。<BR>五、客户端测试：<BR>&nbsp;&nbsp;&nbsp;&nbsp; 客户端我们也可以使用java来进行测试，网上也有资料的，你可以去学习，很简单的。现在为了体现web服务的魅力，我用.NET平台来测试吧，客户端使用c#编写（先要安装.net framework sdk)：<BR>&nbsp;&nbsp;&nbsp; 1）通过wsdl生成web服务代理，在net平台下运行：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wsdl&nbsp; /l:CS&nbsp; /protocol:SOAP&nbsp; /out:SoapTestClient.cs&nbsp; <A href="http://localhost/WebserviceTest/services/SoapTest?wsdl"><FONT color=#000080>http://localhost/WebserviceTest/services/SoapTest?wsdl</FONT></A>&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们通过wsdl得到了一个cs文件SoapTestClient.cs（当前目录），你可以打开cs文件，研究一下里面的代码，那个getStr(string name)就是我们需要调用的方法，我们的客户端通过调用该方法就可以调用服务器端的方法，内部的转化wsdl.exe工具已经帮我们完成了，axis下的WSDL2Java工具也是一样的功能，可以参考我上面所说的关于Axis的内核内容<BR>&nbsp;&nbsp;&nbsp;2)编译cs文件成程序集dll：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; csc /target:library /r:System.Web.Services.dll&nbsp; /r:System.Xml.dll SoapTestClient.cs<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后我们等到了一个dll文件SoapTestClient.dll，客户端程序通过调用它就行了<BR>&nbsp;&nbsp;&nbsp;3）编写客户端应用程序SoapTestClientApp.cs<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using System;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;namespache&nbsp; jws.client{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public class SoapTestClientApp{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public SoapTestClientApp(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void&nbsp;Main(string[] args){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(args.Length!=1){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Usage:SoapTestClientApp &lt;name&gt;");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Environment.Exit(1);<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;&nbsp;&nbsp;&nbsp;SoapTestService&nbsp; st_service=new SoapTestService();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;st_service.getStr("Wang wenyin");<BR>&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;}<BR>４）编译文件csc&nbsp; /r:SoapTestClient.dll&nbsp; SoapTestClientApp.cs<BR>&nbsp;&nbsp;&nbsp;&nbsp;运行SoapTestClientApp<BR>输出结果：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hello,Wang wenyin<BR>与预期结果相符。<BR></P><img src ="http://www.blogjava.net/Victor/aggbug/23177.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-09 17:12 <a href="http://www.blogjava.net/Victor/articles/23177.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于JAX-RPC的快速Web服务开发方案 </title><link>http://www.blogjava.net/Victor/articles/23176.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 09 Dec 2005 09:11:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23176.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23176.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23176.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23176.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23176.html</trackback:ping><description><![CDATA[前两天写了两篇Blog<A id=_54207c63bdac775_HomePageDays_DaysList__ctl2_DayItem_DayList__ctl1_TitleUrl href="http://blog.csdn.net/jwsh1984/archive/2005/02/09/285093.aspx"><FONT color=#000080>使用SOAP开发java web服务--Axis开发方案</FONT></A> ，<A id=_54207c63bdac775_HomePageDays_DaysList__ctl0_DayItem_DayList__ctl0_TitleUrl href="http://blog.csdn.net/jwsh1984/archive/2005/02/11/285574.aspx"><FONT color=#000080>深入探索SOAP1.1--使用SAAJ1.2.1</FONT></A> ，有些读者可能对Java的web服务开发抱怨了，怎的那么复杂呢？不如在net平台下敲几个键就行了，：），请先不用着急，前两天我的Blog只是为了让各位读者深入了解一下什么是SOAP，以及其在Java中的封装接口实现，今天我们来讨论一下，一种高层的web服务实现方案，那就是基于JAX-RPC的服务实现。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JAX-RPC 1.1规范没有定义用于实现基于JAX-RPC的服务的任何API。基于JAX-RPC的服务均可使用Java类或使用WSDL文档实现，这两种情况，JAX-RPC均不为其服务端实现访问和使用部署的服务指定任何要求。基于JAX-RPC的服务于开发RMI应用程序极为相似（JAX-RPC是使用基于SOAP的RPC和WSDL机制调用异源环境中运行的web服务，集成了远程过程调用（RPC）的性能，通过提供Java与XML/WSDL之间所需的映射，封装了底层SOAP包装和消息交换的复杂操作）。<BR><BR>一、基于Java类开发JAX-RPC服务。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 步骤与RMI开发基本类似：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1）定义远程接口（服务定义）；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）实现方案远程接口（服务实现方案）；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3）配置服务；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4）生成部署文件；<BR>&nbsp;&nbsp;&nbsp;&nbsp;我实现的例子：&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;1、定义远程接口（StudentInfoIF.java）：<BR>/**<BR>&nbsp;*StudentInfoIF.java<BR>&nbsp;*Copyright 2005-2-10<BR>&nbsp;<A href="mailto:*@author"><FONT color=#000080>*@author</FONT></A> 阿飞<BR>&nbsp;*/<BR>package edu.cun.jws.jaxrpc;
<P>import java.rmi.Remote;<BR>import java.rmi.RemoteException;</P>
<P>/**<BR>&nbsp;*该接口主要用于jax-rpc服务定义（远程接口）<BR>&nbsp;*/<BR>public interface StudentInfoIF extends Remote{<BR>&nbsp;/**<BR>&nbsp; <A href="mailto:*@param"><FONT color=#000080>*@param</FONT></A> studentName 这是一个学生名字的字符串表达<BR>&nbsp; <A href="mailto:*@return"><FONT color=#000080>*@return</FONT></A> String 返回学生的信息<BR>&nbsp; */<BR>&nbsp;&nbsp;&nbsp; public String getStudentInfo(String studentName) throws RemoteException;<BR>}<BR>&nbsp;&nbsp;&nbsp; 2、实现远程接口（StudentInfoImpl.java）：<BR>/**<BR>&nbsp;*StudentInfoImpl.java<BR>&nbsp;*Copyright 2005-2-10<BR>&nbsp;<A href="mailto:*@author"><FONT color=#000080>*@author</FONT></A> 阿飞<BR>&nbsp;*/<BR>package edu.cun.jws.jaxrpc;<BR>&nbsp;<BR>import java.rmi.Remote;<BR>import java.rmi.RemoteException;</P>
<P>/**<BR>&nbsp;*实现远程接口StudentInfoIF，服务实现方案<BR>&nbsp;<A href="mailto:*@see"><FONT color=#000080>*@see</FONT></A> StudentInfoIF<BR>&nbsp;*/<BR>public class StudentInfoImpl implements StudentInfoIF{<BR>&nbsp;<BR>&nbsp;public String getStudentInfo(String studentName){<BR>&nbsp;&nbsp;System.out.println("Your name:"+studentName);<BR>&nbsp;&nbsp;return new String(" Hello");<BR>&nbsp;}<BR>}<BR>&nbsp;&nbsp; 对上面的源文件进行编译：java *.java&nbsp; -d .<BR>最后在当前文件夹得到一个edu文件夹，里面就是我们的需要的服务的字节类。<BR>&nbsp;&nbsp; 3、准备部署<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于是使用JWSDP1.5的开发工具，读者请到SUN网站下载JWSDP开发工具<A href="http://java.sun.com/webservices/downloads/webservicespack.html"><FONT color=#000080>http://java.sun.com/webservices/downloads/webservicespack.html</FONT></A>：<BR>我们使用的是jwsdp安装路径/jaxrpc/bin中的工具wsdeploy，该工具先通过调用wscompile&nbsp; -gen:server来完成编译(由于现在我们部署的对象是Tomcat，所以我们要调用wsdeploy，如果是标准的J2EE实现方案的容器的话，使用wscompile就行了，具体请看帮助文档）：&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因为使用的是wsdeploy，所以要先编写一个jaxrpc-ri.xml文件，针对我们这次的例子：<BR>&lt;?xml version="1.0" encoding="UTF-8"?&gt;&nbsp; <BR>&lt;webServices&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns="<A href="http://java.sun.com/xml/ns/jax-rpc/ri/dd"><FONT color=#000080>http://java.sun.com/xml/ns/jax-rpc/ri/dd</FONT></A>"&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version="1.0"&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; targetNamespaceBase="<A href="http://www.cun.edu.cn/jws/wsdl"><FONT color=#000080>http://www.cun.edu.cn/jws/wsdl</FONT></A>"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&lt;------------这个是你自己命名的空间<BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typeNamespaceBase="<A href="http://www.cun.edu.cn/jws/types"><FONT color=#000080>http://www.cun.edu.cn/jws/types</FONT></A>"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&lt;------------也是自己定义的</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; urlPatternBase="/StudentInfo"&gt;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;endpoint&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name="StudentInfo"&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interface="edu.cun.jws.jaxrpc.StudentInfoIF"&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;<FONT color=#ff0000>&lt;-----------这个是我们提供的接口</FONT>&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; implementation="edu.cun.jws.jaxrpc.StudentInfoImpl"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#ff0000>&lt;-----------实现接口的类</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;endpointMapping&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endpointName="StudentInfo"&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; urlPattern="/Info"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; <FONT color=#ff0000>&lt;-----------等于是web.xml文件中的url-pattern</FONT><BR>&lt;/webServices&gt;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果需要事先对web.xml文件填充的话：<BR>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<BR>&lt;web-app xmlns="<A href="http://java.sun.com/xml/ns/j2ee"><FONT color=#000080>http://java.sun.com/xml/ns/j2ee</FONT></A>"<BR>&nbsp;&nbsp;&nbsp; xmlns:xsi="<A href="http://www.w3.org/2001/XMLSchema-instance"><FONT color=#000080>http://www.w3.org/2001/XMLSchema-instance</FONT></A>"<BR>&nbsp;&nbsp;&nbsp; xsi:schemaLocation="<A href="http://java.sun.com/xml/ns/j2ee"><FONT color=#000080>http://java.sun.com/xml/ns/j2ee</FONT></A> <A href="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"><FONT color=#000080>http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd</FONT></A>"<BR>&nbsp;&nbsp;&nbsp; version="2.4"&gt;<BR>&nbsp;&lt;session-config&gt;<BR>&nbsp;&nbsp;&lt;session-timeout&gt;60&lt;/session-timeout&gt;<BR>&nbsp;&lt;/session-config&gt;<BR>&lt;/web-app&gt;<BR>准备部署工作完毕。<BR>&nbsp;&nbsp;&nbsp; 4、部署（要熟悉j2ee部署规范，这里我的部署对象是Tomcat）<BR>&nbsp;&nbsp;&nbsp;我们创建一个文件夹StudentInfo<BR>以下是文件夹内容<BR>/StudentInfo<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /WEB-INF<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /classes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&nbsp;&lt;-------把我们的服务程序拷到这里，这里是edu文件夹</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /lib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&lt;-------必须的包，</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; web.xml&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&lt;-------上面我们编写的文件</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jaxrpc-ri.xml&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&lt;-------上一步编写的文件<BR></FONT><FONT color=#000000>注意这里必须的包包括JAX-RPC相关的包，还有SAAJ相关的包（JavaMail,JAF,JAXP),我这里用了以下几个包（由于是使用jdk1.5，所以JAXP相关包不在内）：activation.jar(JAF),jaxrpc-api.jar,jaxrpc-impl.jar,jaxrpc-spi.jar,mail.jar,saaj-api.jar,saaj-impl.jar；<BR>好了之后，我们打包成一个war文件（由于wsdeploy需要的输入文件是一个war存档文件）：<BR>转换到到StudentInfo目录下，设置好运行wsdeploy的运行路径，运行：<BR>&nbsp;&nbsp;&nbsp; jar cvf&nbsp;&nbsp; StudentInfo.war&nbsp; *<BR>&nbsp;&nbsp;&nbsp; wsdeploy&nbsp; -o StudentInfoApp.war StudentInfo.war<BR>&nbsp;&nbsp;&nbsp;&nbsp;最后我们得到一个StudentInfoApp.war文件，该文件就是我们需要的部署web服务的程序。我们把它拷到Tomcat的webapps目录下，就自动部署好了<BR>&nbsp;&nbsp;&nbsp; 5、测试<BR>&nbsp;&nbsp;&nbsp; 在浏览器中输入：<A href="http://localhost:8080/StudentInfoApp/Info"><FONT color=#000080>http://localhost:8080/StudentInfoApp/Info</FONT></A>&nbsp;<BR><BR>测试成功，web服务部署完毕，客户端的测试的话，自己编写，可以使用java,也可以使用net平台等作为客户端，请参考我的例子：<A id=_54207c63bdac775_HomePageDays_DaysList__ctl2_DayItem_DayList__ctl1_TitleUrl href="http://blog.csdn.net/jwsh1984/archive/2005/02/09/285093.aspx"><FONT color=#000080>使用SOAP开发java web服务--Axis开发方案</FONT></A> ，就是使用C#作为服务的调用客户端。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;ps：基于JAX-RPC的web服务开发就是这么简单，很多内部转换的内容都已经自动完成了，我们可以把精力集中于商业逻辑上来，欢迎大家加入Java Web Service的开发行列来。<BR></FONT></P><img src ="http://www.blogjava.net/Victor/aggbug/23176.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-09 17:11 <a href="http://www.blogjava.net/Victor/articles/23176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入探索SOAP1.1--使用SAAJ1.2.1 </title><link>http://www.blogjava.net/Victor/articles/23174.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 09 Dec 2005 09:08:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/23174.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/23174.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/23174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/23174.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/23174.html</trackback:ping><description><![CDATA[本文的预定作者应该对SOAP1.1的基本规范有所了解，并熟悉了j2ee的基本开发，如果不熟悉的话，可以看一下我的Blog：<A id=_e8123ab36c7c1c3a_HomePageDays_DaysList__ctl1_DayItem_DayList__ctl1_TitleUrl href="http://blog.csdn.net/jwsh1984/archive/2005/02/09/285093.aspx"><FONT color=#000080>使用SOAP开发java web服务--Axis开发方案</FONT></A>&nbsp;，详细标准可以查看w3c的官方网站，连接如下：<A href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/">http://www.w3.org/TR/2000/NOTE-SOAP-20000508/</A>&nbsp;。本文主要是探讨SAAJ（SOAP with Attachment API for Java),JAXM(Java API for XML Messaging)，了解SOAP在j2ee开发中的作用与其接口。JAXM和SAAJ均支持针对B2B和Web服务应用程序、基于XML的消息交换，支持诸多行业标准，包括SOAP和ebXML。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAAJ是JWSDP的组成部分之一，JWSDP目前已经更新到了1.5版，SAAJ可以从SUN的网站单独下载：<A href="http://java.sun.com/xml/downloads/saaj.html">http://java.sun.com/xml/downloads/saaj.html</A>，JAXM是用于XML消息交换的标准，不在JWSDP1.5的发行包内，可以另外下载：<A href="http://java.sun.com/xml/jaxm/downloads/index.html">http://java.sun.com/xml/jaxm/downloads/index.html</A>&nbsp;，由于SAAJ是基于JavaMail<SUP><FONT size=-2>TM</FONT></SUP> API (1.2)&nbsp;、 JavaBeans<SUP><FONT size=-2>TM</FONT></SUP> Activation Framework (JAF) (1.1.3)和JAXP(1.2.6) 的，所以也要把他们下载下来，SUN的官方网站都有提供的，好了，现在我们得到了以下包（按我下载的名称）：activation.jar（JAF），jaxm-api.jar（JAXM），mail.jar(JavaMail),saaj-api.jar(SAAJ),saaj-impl.jar(SAAJ)，我的JAXP已经包括在jdk1.5中了。另外还要有个支持Servlet的容器。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好了，准备工作完毕了，下面开始我们的学习：<BR>一、&nbsp;SAAJ可用于将XML文档作为SOAP消息发送和接收，而无需JAXM提供商的基础程序结构，也无需处理基于SOAP的HTTP请求/响应。SAAJ最初是JAXM1.0API软件包的组成部分，而从JAXM1.1开始，该软件包更名为SAAJ1.1API。<BR>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; 以下我们先了解一个简单的SOAP1.1消息：<BR>POST /StockQuote HTTP/1.1<BR>Host: <A href="http://www.stockquoteserver.com/">www.stockquoteserver.com</A><BR>Content-Type: text/xml; charset="utf-8"<BR>Content-Length: nnnn<BR>SOAPAction: "Some-URI"
<P>&lt;SOAP-ENV:Envelope<BR>&nbsp; xmlns:SOAP-ENV="<A href="http://schemas.xmlsoap.org/soap/envelope/">http://schemas.xmlsoap.org/soap/envelope/</A>"<BR>&nbsp; SOAP-ENV:encodingStyle="<A href='http://schemas.xmlsoap.org/soap/encoding/"/'>http://schemas.xmlsoap.org/soap/encoding/"/</A>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&lt;------------这是信封标志</FONT><BR>&nbsp;&nbsp; &lt;SOAP-ENV:Header&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#ff0000>&lt;------------这是消息头标志</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;t:Transaction<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:t="some-URI"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOAP-ENV:mustUnderstand="1"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/t:Transaction&gt;<BR>&nbsp;&nbsp; &lt;/SOAP-ENV:Header&gt;<BR>&nbsp;&nbsp; &lt;SOAP-ENV:Body&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#ff0000>&lt;------------这是主体标志<BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;m:GetLastTradePrice xmlns:m="Some-URI"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;symbol&gt;DEF&lt;/symbol&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/m:GetLastTradePrice&gt;<BR>&nbsp;&nbsp; &lt;/SOAP-ENV:Body&gt;<BR>&lt;/SOAP-ENV:Envelope&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAAJ API为我们提供了一个高层次SOAP消息封装接口，如:信封接口：javax.xml.soap.SOAPEnvelope，并提供了操作getHeader(),该方法返回一个消息头：javax.xml.soap.SOAPHeader。所以，通过调用SAAJ的接口函数，我们就可以对SOAP消息进行操作。<BR>二、现在我们来讨论一下在不使用消息交换提供程序的JAXM时，应用程序客户端通过SOAP直接与其远程伙伴发送和接收消息操作（定义了点到点的交互作用和同步通信模型，其中发送方和接收方以请求和响应的形式交换消息。发送方发送消息并等待锁定目标位置的响应）。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;发送方步骤：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1）创建SOAP连接；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）创建消息工厂；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3）创建消息；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4）填充消息；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5）添加消息；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6）添加SOAP附件；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7）发送消息并接收响应；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8）关闭提供程序连接；<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一下是我写的一个例子：<BR>&nbsp;&nbsp;<BR>/**<BR>&nbsp;*Sender.java<BR>&nbsp;*Copyright 2005-2-10<BR>&nbsp;*/<BR>import javax.xml.soap.SOAPConnectionFactory;<BR>import javax.xml.soap.SOAPException;<BR>import javax.xml.soap.SOAPConnection;<BR>import javax.xml.soap.MessageFactory;<BR>import javax.xml.soap.SOAPMessage;<BR>import javax.xml.soap.SOAPPart;<BR>import javax.xml.soap.SOAPEnvelope;<BR>import javax.xml.soap.SOAPHeader;<BR>import javax.xml.soap.SOAPBody;<BR>import javax.xml.soap.SOAPBodyElement;<BR>import javax.xml.soap.SOAPHeaderElement;<BR>import javax.xml.soap.Name;<BR>import javax.xml.soap.SOAPElement;<BR>import java.net.URL;<BR>import javax.xml.messaging.URLEndpoint;<BR>import javax.activation.DataHandler;<BR>import java.io.IOException;<BR>&nbsp;<BR>public class Sender{<BR>&nbsp;public SOAPMessage getMessage() throws SOAPException,Exception{<BR>&nbsp;&nbsp;//消息工厂<BR>&nbsp;&nbsp;MessageFactory msgFactory=MessageFactory.newInstance();<BR>&nbsp;&nbsp;SOAPMessage message=msgFactory.createMessage();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//获得一个SOAPPart对象<BR>&nbsp;&nbsp;SOAPPart soapPart=message.getSOAPPart();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//获得信封<BR>&nbsp;&nbsp;SOAPEnvelope soapEnvelope=soapPart.getEnvelope();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//获得消息头<BR>&nbsp;&nbsp;SOAPHeader soapHeader=soapEnvelope.getHeader();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//获得SOAP主体<BR>&nbsp;&nbsp;SOAPBody soapBody=soapEnvelope.getBody();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//添加头元素<BR>&nbsp;&nbsp;SOAPHeaderElement headerElement=soapHeader.addHeaderElement(<BR>&nbsp;&nbsp;&nbsp;soapEnvelope.createName("StudentNo","stu","<A href="http://www.cun.edu.cn/jws">http://www.cun.edu.cn/jws</A>"));<BR>&nbsp;&nbsp;headerElement.addTextNode("JWS0229043");<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//添加消息主体<BR>&nbsp;&nbsp;Name bodyName=soapEnvelope.createName("getStudentInfo","stu","<A href="http://www.cun.edu.cn/jws">http://www.cun.edu.cn/jws</A>");<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;SOAPBodyElement bodyElement=soapBody.addBodyElement(bodyName);<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;Name eleName=soapEnvelope.createName("StudentName");<BR>&nbsp;&nbsp;SOAPElement se=bodyElement.addChildElement(eleName);<BR>&nbsp;&nbsp;se.addTextNode("Wang wenyin");<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//添加SOAP附件<BR>&nbsp;&nbsp;URL url=new URL("<A href="http://img20.photo.163.com/gdanthrowwy/5123911/80707051.jpg">http://img20.photo.163.com/gdanthrowwy/5123911/80707051.jpg</A>");<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;DataHandler dataHandler=new DataHandler(url);//use the JAF<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;message.addAttachmentPart(message.createAttachmentPart(dataHandler));<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//更新SOAP消息<BR>&nbsp;&nbsp;message.saveChanges();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;return message;<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public void send(SOAPMessage message) throws SOAPException,IOException{<BR>&nbsp;&nbsp;//创建SOAP连接<BR>&nbsp;&nbsp;SOAPConnectionFactory scf=SOAPConnectionFactory.newInstance();<BR>&nbsp;&nbsp;SOAPConnection sc=scf.createConnection();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;//发送SOAP消息到目的地，并返回一个消息<BR>&nbsp;&nbsp;URLEndpoint urlEndpoint=new URLEndpoint("<A href="http://localhost/saaj/StudentInfoServlet">http://localhost/saaj/StudentInfoServlet</A>");<BR>&nbsp;&nbsp;SOAPMessage response=sc.call(message,urlEndpoint);<BR>&nbsp;&nbsp;if(response!=null){<BR>&nbsp;&nbsp;&nbsp;//输出SOAP消息到控制台<BR>&nbsp;&nbsp;&nbsp;System.out.println("Receive SOAP message from localhost:");<BR>&nbsp;&nbsp;&nbsp;response.writeTo(System.out);<BR>&nbsp;&nbsp;}else{<BR>&nbsp;&nbsp;&nbsp;System.err.println("No response received from partner!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;sc.close();&nbsp;<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public static void main(String[] args) throws SOAPException,Exception{<BR>&nbsp;&nbsp;Sender sender=new Sender();<BR>&nbsp;&nbsp;SOAPMessage message=sender.getMessage();<BR>&nbsp;&nbsp;sender.send(message);<BR>&nbsp;}<BR>}<BR>&nbsp;&nbsp;&nbsp;&nbsp; 然后编译，注意classpath变量的设置要把以上的那些包加进去（可以设置一个脚本来完成，熟悉Ant的话，那就更加简单了）。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 编译成功后，我们等到了一个Sender.class文件，这是发送方文件。当你运行java Sender的时候，就会把SOAP消息发向我们的同伴<A href="http://localhost/saaj/StudentInfoServlet">http://localhost/saaj/StudentInfoServlet</A>&nbsp;，并等待返回。以下我们继续编写一个Servlet好接收刚才发送的消息。<BR>三、接收方Servlet应用程序saaj.war。<BR>/**<BR>&nbsp;*JAXMReceiveServlet.java<BR>&nbsp;*Copyright 2005-2-10<BR>&nbsp;*/<BR>import javax.xml.messaging.JAXMServlet;<BR>import javax.xml.messaging.ReqRespListener;<BR>import javax.xml.soap.MessageFactory;<BR>import javax.servlet.ServletException;<BR>import javax.xml.soap.SOAPMessage;<BR>import javax.xml.soap.SOAPEnvelope;<BR>import javax.servlet.ServletConfig;<BR>import java.io.FileOutputStream;<BR>import java.io.File;</P>
<P>public class JAXMReceiveServlet extends JAXMServlet implements ReqRespListener{<BR>&nbsp;static MessageFactory mf=null;<BR>&nbsp;//创建一个消息工厂<BR>&nbsp;static{<BR>&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;&nbsp;mf=MessageFactory.newInstance();<BR>&nbsp;&nbsp;}catch(Exception e){<BR>&nbsp;&nbsp;&nbsp;e.printStackTrace();<BR>&nbsp;&nbsp;}<BR>&nbsp;};<BR>&nbsp;<BR>&nbsp;public void init(ServletConfig sc) throws ServletException{<BR>&nbsp;&nbsp;super.init(sc);<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;//处理传过来的SOAP消息，并返回一个SOAP消息<BR>&nbsp;public SOAPMessage onMessage(SOAPMessage msg){<BR>&nbsp;&nbsp;SOAPMessage resp=null;<BR>&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;&nbsp;System.out.println("传入的消息：");<BR>&nbsp;&nbsp;&nbsp;msg.writeTo(new FileOutputStream(new File("../webapps/soapmessage.xml")));<BR>&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;//创建一个返回消息<BR>&nbsp;&nbsp;&nbsp;resp=mf.createMessage();<BR>&nbsp;&nbsp;&nbsp;SOAPEnvelope se=resp.getSOAPPart().getEnvelope();<BR>&nbsp;&nbsp;&nbsp;se.getBody().addChildElement(<BR>&nbsp;&nbsp;&nbsp;&nbsp;se.createName("ResponseMessage")).addTextNode("Received Message,Thanks");<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;return resp;<BR>&nbsp;&nbsp;}catch(Exception e){<BR>&nbsp;&nbsp;&nbsp;e.printStackTrace();<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;return resp;<BR>&nbsp;}<BR>}<BR>&nbsp;&nbsp; 然后把相关的classpath添加进去，编译（不会的话，自己去查有关Servlet的编程，篇幅有限）<BR>&nbsp;&nbsp;&nbsp; web.xml部署文件：<BR>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<BR>&lt;web-app xmlns="<A href="http://java.sun.com/xml/ns/j2ee">http://java.sun.com/xml/ns/j2ee</A>"<BR>&nbsp;&nbsp;&nbsp; xmlns:xsi="<A href="http://www.w3.org/2001/XMLSchema-instance">http://www.w3.org/2001/XMLSchema-instance</A>"<BR>&nbsp;&nbsp;&nbsp; xsi:schemaLocation="<A href="http://java.sun.com/xml/ns/j2ee">http://java.sun.com/xml/ns/j2ee</A> <A href="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd</A>"<BR>&nbsp;&nbsp;&nbsp; version="2.4"&gt;<BR>&nbsp;&lt;servlet&gt;<BR>&nbsp;&nbsp;&lt;servlet-name&gt;StudentInfoServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&lt;servlet-class&gt;JAXMReceiveServlet&lt;/servlet-class&gt;<BR>&nbsp;&nbsp;&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<BR>&nbsp;&lt;/servlet&gt;<BR>&nbsp;<BR>&nbsp;&lt;servlet-mapping&gt;<BR>&nbsp;&nbsp;&lt;servlet-name&gt;StudentInfoServlet&lt;/servlet-name&gt;<BR>&nbsp;&nbsp;&lt;url-pattern&gt;/StudentInfoServlet&lt;/url-pattern&gt;<BR>&nbsp;&lt;/servlet-mapping&gt;<BR>&lt;/web-app&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 至此&nbsp;我们的基本工作完成了，部署好程序saaj，当你在浏览器访问&nbsp; <A href="http://localhost/saaj/StudentInfoServlet">http://localhost/saaj/StudentInfoServlet</A>&nbsp;，将会返回一个错误信息，因为这里使用的是SOAP协议。<BR>四、测试<BR>&nbsp;&nbsp;&nbsp;&nbsp; 如果部署成功的话，那开始我们的测试。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 我们继续第二步的步骤，java Sender，接着我们就等待，我们在发送方创建了一个SOAP消息（有个jpg图片的附件），并发送到Servlet容器中（我的是tomcat），在服务器方接送到消息，并在webapps文件夹下创建soapmessage.xml文件，把接收到的SOAP信息写进去，并返回一个soap消息。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 等过了一段时间后，sender方会返回一个soap格式的xml文件，在控制台上输出。<BR><BR><BR>好了，今天的关于SOAP的深入探索就到这里了。<BR></P><img src ="http://www.blogjava.net/Victor/aggbug/23174.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-12-09 17:08 <a href="http://www.blogjava.net/Victor/articles/23174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现安全的AXIS Web服务，第2部分(转)</title><link>http://www.blogjava.net/Victor/articles/6673.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 24 Jun 2005 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/6673.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/6673.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/6673.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/6673.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/6673.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR>
<TD width=8><IMG height=21 alt="" src="http://www.ibm.com/i/c.gif" width=5></TD>
<TD width=160>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#N10075">Web 服务安全性相关技术和开发工具</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#N100A2">SOAP消息的签名和验证</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#N1010D">在AXIS下实现WS-Security的应用框架</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#N10226">总结</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR><!--Standard links for every dw-article-->
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#resources">参考资料 </A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#author1">关于作者</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#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></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=150 bgColor=#000000 colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#author1"><NAME>陈亚强</NAME></A><BR>高级软件工程师<BR>2004 年 8 月 </P>
<BLOCKQUOTE>本文将首先简单介绍Web 服务安全性相关技术和开发工具，然后介绍了使用WSSecurity工具SOAP消息进行签名和验证的方法；接下来深入讨论了使用现成的WS-Security工具，结合Handler模型开发一个axis下实现WS-Security的通用应用框架。</BLOCKQUOTE>
<P>本文是J2EE Web服务开发系列文章的第十三篇，本文将首先简单介绍Web 服务安全性相关技术和开发工具，然后介绍了使用WSSecurity工具SOAP消息进行签名和验证的方法；接下来深入讨论了使用现成的WS-Security工具，结合Handler模型开发一个axis下实现WS-Security的通用应用框架。</P>
<P>阅读本文前您需要以下的知识和工具：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>Apache axis1.1，并且会初步使用； 
<LI>Tomcat 5.0.16以上, 并且会初步使用； 
<LI>SOAP消息（SOAP Message）编程知识； 
<LI>Java安全编程基础知识； 
<LI>JAX-RPC编程基础知识； 
<LI>Servlet的开发经验； 
<LI>Sun提供的JAX-RPC参考实现（jaxrpc-impl.jar，在J2EESDK1.4或者JWSDP1.4中可找到）； 
<LI>一个JSSE安全提供者（如ISNetworks）； 
<LI>Trust Services Integration Kit，可在http://www.xmltrustcenter.org上获得。 </LI></UL>
<P>本文的参考资料见 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">参考资料</A>。 </P>
<P>本文的全部代码在这里 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/src.rar" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">下载</A>。 </P>
<P><A name=N10075><SPAN class=atitle2>Web 服务安全性相关技术和开发工具</SPAN></A><BR>Web 服务安全性规范是一套可以帮助 Web 服务开发者保证 SOAP 消息交换的安全的机制。WS-Security 特别描述了对现有的 SOAP 消息传递的增强，从而通过对 SOAP 消息应用消息完整性、消息机密性和单消息认证提供了保护级别。这些基本机制可以通过各种方式联合，以适应构建使用多种加密技术的多种安全性模型。</P>
<P>围绕Web服务的安全，有很多相关的技术，比如WS-Security，WS-Trace等，另外，还有以下相关技术：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>XML Digital Signature（XML数字签名） 
<LI>XML Encryption （XML加密） 
<LI>XKMS (XML Key Management Specification) 
<LI>XACML (eXtensible Access Control Markup Language) 
<LI>SAML (Secure Assertion Markup Language) 
<LI>ebXML Message Service Security 
<LI>Identity Management &amp; Liberty Project </LI></UL>
<P>由于本文是一个实例性文章，故不对WS-Security做详细的探讨，你可以在develperWorks Web 服务安全专题找到许多相关资料（见参考资料）。</P>
<P>Trust Services Integration Kit提供了一个WS-Security实现。你可以从http://www.xmltrustcenter.org获得相关库文件，分别是wssecurity.jar和tsik.jar。wssecurity.jar中包含一个WSSecurity类，可以使用它来对XML进行数字签名和验证，加密与解密。</P>
<P>下面我们使用WS-Security来对SOAP消息进行数字签名，然后再进行验证。</P>
<P><A name=N100A2><SPAN class=atitle2>SOAP消息的签名和验证</SPAN></A><BR></P>
<P><A name=N100A8><SPAN class=atitle3>使用WSSecurity对SOAP消息数字签名</SPAN></A><BR>在对SOAP消息进行签名前，首先生成一个keystore。keystore包含了进行数字签名所需要的身份信息。通过以下批处理脚本来创建keystore：</P><A name=N100B1><B>例程1 创建keystore（server.keystore）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
set SERVER_DN="CN=hellking-Server, OU=huayuan, O=huayuan, L=BEIJINGC, S=BEIJING, C=CN"
set KS_PASS=-storepass changeit
set KS_TYPE=-storetype JKS
set KEYINFO=-keyalg RSA
#生成服务器端keystore。
keytool -genkey -dname %SERVER_DN% %KS_PASS% %KS_TYPE% -keystore 
server.keystore %KEYINFO% -keypass changeit
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>SignAndVerifySoap类中包含了一个对XML进行签名的方法，它就是sign（），这个方法将对SOAP消息进行签名，然后输出和WS-Security兼容的SOAP消息。下面我们看具体代码。</P><A name=N100BE><B>例程2 对SOAP消息签名</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
	package com.hellking.study.webservice;
import com.verisign.messaging.WSSecurity;
...
public class SignAndVerifySoap {
	
	final String KEY_STORE = "server.keystore";
    final String SOTE_PASS = "changeit";
    final String KEY_ALIAS="mykey";
    final String TARGET_FILE="signed.xml";//签名后的SOAP消息
    final String SOURE_FILE="source.xml";//签名前的SOAP消息
    final String KEY_TYPE="JKS";
	
	/**
	 *对xml进行签名
	 */
	public void sign()
	{	

		try
		{
			System.out.println("开始对SOAP消息进行签名，使用的密匙库：" + KEY_STORE + "\n");
		
		// 获得私有key和相关证书，请参考JAVA安全编程相关书籍
		FileInputStream fileInputStream = new FileInputStream(KEY_STORE);
		System.out.println(java.security.KeyStore.getDefaultType());
		java.security.KeyStore store = java.security.KeyStore.getInstance(KEY_TYPE);
		store.load(fileInputStream, SOTE_PASS.toCharArray());
		PrivateKey key = (PrivateKey)store.getKey(KEY_ALIAS, SOTE_PASS.toCharArray());
		X509Certificate certification = (X509Certificate)store.getCertificate(KEY_ALIAS);

		// 读取XML源文件到文档中
		Document source = readFile(SOURE_FILE);
		SigningKey signingKey = SigningKeyFactory.makeSigningKey(key);
		KeyInfo keyInfo = new KeyInfo();
		keyInfo.setCertificate(certification);

		WSSecurity wsSecurity = new WSSecurity();

		wsSecurity.setPreferredNamespace("http://schemas.xmlsoap.org/ws/2003/06/secext");
		//对SOAP消息进行签名
		wsSecurity.sign(source, signingKey, keyInfo);
		// 保存签名后的SOAP消息	
		writeFile(source, new FileOutputStream(TARGET_FILE));
		System.out.println("把签名后的文件写入： " + TARGET_FILE + "，请查看结果！");
	}
	catch(Exception e)
	{
		e.printStackTrace();
	}		
	}
	</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在执行此程序前，请把wssecurity.jar、source.xml和tsik.jar设置到类路径环境变量中。签名前的SOAP为：</P><A name=N100CB><B>例程3 签名前的SOAP消息（source.xml）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;soapenv:Envelope 
	xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
 &lt;soapenv:Body&gt;
  &lt;ns1:getTax soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://hellking.webservices.com/"&gt;
   &lt;op1 xsi:type="xsd:double"&gt;5000.0&lt;/op1&gt;
  &lt;/ns1:getTax&gt;
 &lt;/soapenv:Body&gt;
&lt;/soapenv:Envelope&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>签名后的SOAP消息如例程4所示。</P><A name=N100D8><B>例程4 签名后的SOAP消息（signed.xml）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
 &lt;soapenv:Header&gt;
&lt;wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"&gt;
&lt;wsse:BinarySecurityToken EncodingType="wsse:Base64Binary" 
ValueType="wsse:X509v3" wsu:Id="wsse-ee805a80-cd95-11d8-9cf9-fd6213c0f8be" 
xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility"&gt;MIICUjCCAbsCBEDB0GIwDQYJKoZIhvcNAQE…VkTkPw==
&lt;/wsse:BinarySecurityToken&gt;
&lt;ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"&gt;
&lt;ds:SignedInfo&gt;
&lt;ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&gt;
&lt;ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/&gt;
&lt;ds:Reference URI="#wsse-ee5308f0-cd95-11d8-9cf9-fd6213c0f8be"&gt;
&lt;ds:Transforms&gt;&lt;ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&gt;
&lt;/ds:Transforms&gt;&lt;ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/&gt;
&lt;ds:DigestValue&gt;ZjRVnI2g7kcX0h9r4JtiltpYQPA=&lt;/ds:DigestValue&gt;&lt;/ds:Reference&gt;
&lt;ds:Reference URI="#wsse-ee4e4e00-cd95-11d8-9cf9-fd6213c0f8be"&gt;
&lt;ds:Transforms&gt;&lt;ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/&gt;
&lt;/ds:Transforms&gt;&lt;ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/&gt;
&lt;ds:DigestValue&gt;moZ0d+8mH1kfNw0VEK39V0Td9EM=&lt;/ds:DigestValue&gt;
&lt;/ds:Reference&gt;
&lt;/ds:SignedInfo&gt;
&lt;ds:SignatureValue&gt;fPpYrf0uNP8W2XVVIQNc3OQt2Wn90M/0uJ0dDZTNRR0NxBBBX36wSXt7NfI5Fmh4ru44Wk34EGI7mqMAE5O0
/wtIlFRJt3zAvA6k3nhgcYj6tn/9kZwwxh1RkFTfTX9xdQ6Xn+P6m+YBm1YEEcTWkJd7XcxdyDEns2kYOhONx1U=
&lt;/ds:SignatureValue&gt;
&lt;ds:KeyInfo&gt;&lt;wsse:SecurityTokenReference&gt;
&lt;wsse:Reference URI="#wsse-ee805a80-cd95-11d8-9cf9-fd6213c0f8be"/&gt;
&lt;/wsse:SecurityTokenReference&gt;
&lt;/ds:KeyInfo&gt;
&lt;/ds:Signature&gt;&lt;/wsse:Security&gt;
&lt;wsu:Timestamp xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility"&gt;
&lt;wsu:Created wsu:Id="wsse-ee4e4e00-cd95-11d8-9cf9-fd6213c0f8be"&gt;2004-07-04T08:41:23Z&lt;/wsu:Created&gt;
&lt;/wsu:Timestamp&gt;&lt;/soapenv:Header&gt;
&lt;soapenv:Body wsu:Id="wsse-ee5308f0-cd95-11d8-9cf9-fd6213c0f8be" 
	xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility"&gt;
  &lt;ns1:getTax soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
  	xmlns:ns1="http://hellking.webservices.com/"&gt;
   &lt;op1 xsi:type="xsd:double"&gt;5000.0&lt;/op1&gt;
  &lt;/ns1:getTax&gt;
 &lt;/soapenv:Body&gt;
&lt;/soapenv:Envelope&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>签名后的SOAP消息中，头部包含了签名信息以及验证SOAP消息所需要的key。&lt;SignedInfo&gt; &lt;/SignedInfo&gt; 描述了已签署的消息内容。&lt;SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/&gt; 指出了签名算法（Signature Method Algorithm）。这个算法被用来将规范算法的输出转换成签名值（Signature Value）。Key Info 元素包含的部分就是数字证书本身。</P>
<P><A name=N100E5><SPAN class=atitle3>对签名的SOAP消息进行验证</SPAN></A><BR>对SOAP消息进行验证就是使用keystore的信息生成TrustVerifier对象，然后调用WSSecurity的verify方法进行验证。</P><A name=N100EE><B>例程5 验证签名后的SOAP消息</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
	/**
	 *验证已经签名的SOAP消息
	 */
	public void verify()
	{
		try
		{
			System.out.println("开始检验SOAP消息，使用的密匙库：" + KEY_STORE + "\n");
		
		// 获得私有key和相关证书，请参考JAVA安全编程相关书籍
		FileInputStream fileInputStream = new FileInputStream(KEY_STORE);

		java.security.KeyStore store = java.security.KeyStore.getInstance(KEY_TYPE);
		store.load(fileInputStream, SOTE_PASS.toCharArray());
	
		// 读取XML源文件到文档中
		Document source = readFile(TARGET_FILE);
		org.xmltrustcenter.verifier.TrustVerifier verifier =
            new org.xmltrustcenter.verifier.X509TrustVerifier(store);

		WSSecurity wsSecurity = new WSSecurity();

        com.verisign.messaging.MessageValidity[] resa =
     wsSecurity.verify(source,verifier,null,null);
       System.out.println("检验结果：");
	   for (int len = 0; len &lt; resa.length; len++){
      System.out.println("result[" + len + "] = " + (resa[len].isValid()?"验证通过":"验证不通过"));
    }
    }

    catch(Exception e)
	{
		e.printStackTrace();
	}
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>执行SignAndVerifySoap的verify方法，可以看到类似以下的结果。</P>
<P><A name=N100FD><B>图1 对SOAP消息进行验证</B></A><BR><IMG height=272 alt="图1 对SOAP消息进行验证" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/1.gif" width=505 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P><A name=N1010D><SPAN class=atitle2>在AXIS下实现WS-Security的应用框架</SPAN></A><BR>待开发的应用开发框架基于Handler实现，将达到以下目标：此框架基于JAX-RPC环境下实现WS-Security应用，它可以部署到任何需要实现WS-Security的axis环境下的Web服务应用中，同时具体的应用程序不做任何编码修改。</P>
<P>由于此基于Handler实现，我们有必要回顾一下Handler的一些基础知识。</P>
<P>SOAP消息Handler能够访问代表RPC请求或者响应的SOAP消息。在JAX-RPC技术中，SOAP消息Handler可以部署在服务端，也可以在客户端使用。</P>
<P>SOAP消息Handler非常像Servlet技术中的Filter，它们共同的特点是请求发送到目标前，Handler/Filter可以截取这些请求，并对请求做一些处理，从而达到一些辅助的功能。多个Handler可以组成一个Handler链，链上的每个Handler都完成某个特定的任务。比如有的Handler进行权限验证，有的Handler进行日志处理等。关于Handler更详细的介绍，请参考本系列文章《 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-handler/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">J2EE Web服务开发系列之六: 使用Handler来增强Web服务的功能</A>》。 </P>
<P><A name=N10123><SPAN class=atitle3>实现原理</SPAN></A><BR>图2是此例子具体实现原理图。</P>
<P><A name=N1012E><B>图2 Handler结合WSSecurity实现Web服务安全的工作原理</B></A><BR><IMG height=433 alt="图2 Handler结合WSSecurity实现Web服务安全的工作原理" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis2/2.gif" width=662 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P>处理流程如下：</P>
<P>1、 客户端（WSSClient）发出调用Web服务请求；</P>
<P>2、 客户端Handler（WSSecurityClientHandler）截获请求的SOAP消息；</P>
<P>3、 客户端Handler对截获的SOAP消息进行数字签名（使用client.keystore作为签名依据）；</P>
<P>4、 客户端Handler对签名后的SOAP消息进行加密（使用RSA算法加密）；</P>
<P>5、 被加密的SOAP消息通过互联网传送到目标Web服务端口；</P>
<P>6、 服务器端Handler（WSSecurityServerHandler）截获加密的SOAP消息；</P>
<P>7、 服务器端Handler对加密的SOAP消息进行解密；</P>
<P>8、 服务器端Handler对SOAP消息进行身份验证（server.truststore包含了所信任的身份信息），如果验证不通过，将抛出异常；</P>
<P>9、 服务器端Handler删除被解密后的SOAP消息中与WS-Security相关的元素；</P>
<P>10、 解密后的原始SOAP消息被发送到目标Web服务端口（如TaxService）；</P>11、 目标Web服务对Web服务请求进行处理，然后返回响应的SOAP消息； 
<P>12、 服务器端Handler截获响应的SOAP消息；</P>
<P>13、 服务器端Handler对截获的SOAP消息进行数字签名（使用server.keystore作为签名依据）；</P>
<P>14、 服务器端Handler对签名后的SOAP消息进行加密（使用RSA算法加密）；</P>
<P>15、 被加密的SOAP消息通过互联网传送到目客户端；</P>
<P>16、 客户端Handler截获加密的SOAP消息；</P>
<P>17、 客户端Handler对加密的SOAP消息进行解密；</P>
<P>18、 客户端Handler对SOAP消息进行身份验证（client.truststore包含了所信任的身份信息），如果验证不通过，将抛出异常；</P>
<P>19、 客户端Handler删除被解密后的SOAP消息中与WS-Security相关的元素；</P>
<P>20、 被解密后的SOAP消息发送到目标客户端，客户端输出调用结果。</P>
<P>从上面可以看出，在一个SOAP调用回合中，要对SOAP消息进行四次处理。基本上都是"签名'加密'解密'验证"的过程。</P>
<P><A name=N1017D><SPAN class=atitle3>创建相关密匙库</SPAN></A><BR>客户端和服务端都有相关的密匙库，其中：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>client.keystore：客户端自身的身份信息； 
<LI>client.truststore：客户端所信任的身份信息，在此例中也就是包含了服务器的身份信息； 
<LI>server.keystore：服务器自身的身份信息； 
<LI>server.truststore：服务器所信任的身份信息（即客户端身份信息）。 </LI></UL>
<P>你可以使用以下的批处理脚本创建上面四个密匙库。</P><A name=N10198><B>例程6 创建相关密匙库（gen-cer-store.bat）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
set SERVER_DN="CN=hellking-Server, OU=huayuan, O=huayuan, L=BEIJINGC, S=BEIJING, C=CN"
set CLIENT_DN="CN=hellking-Client, OU=tsinghua, O=tsinghua, L=BEIJING, S=BEIJING, C=CN"
set KS_PASS=-storepass changeit
set KEYINFO=-keyalg RSA
#生成server.keystore。
keytool -genkey -dname %SERVER_DN% %KS_PASS% -keystore server.keystore %KEYINFO% -keypass changeit
#从server.keystore导出数字证书。
keytool -export -file test_axis.cer %KS_PASS% -keystore server.keystore
#从服务器的数字证书导出到客户端信任的truststore中。
keytool -import -file test_axis.cer %KS_PASS% -keystore client.truststore -alias serverkey -noprompt

#生成client.keystore。
keytool -genkey -dname %CLIENT_DN% %KS_PASS% -keystore client.keystore %KEYINFO% -keypass changeit
#从client.keystore导出数字证书。
keytool -export -file test_axis.cer %KS_PASS% -keystore client.keystore
#从客户端的数字证书导出到服务器信任的truststore中。
keytool -import -file test_axis.cer %KS_PASS% -keystore server.truststore -alias clientkey -noprompt

#end
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101A2><SPAN class=atitle3>签名、加密、解密、身份验证的实现</SPAN></A><BR>对SOAP消息的签名、加密、解密、身份验证都放在一个名为WSSHelper的类中进行。</P><A name=N101AB><B>例程7 签名、加密、解密、身份验证功能的实现――WSSHelper.java</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;
import com.verisign.messaging.WSSecurity;
...
public class WSSHelper {
	static String PROVIDER="ISNetworks";//JSSE安全提供者。	
   //添加JSSE安全提供者，你也可以使用其它安全提供者。只要支持DESede算法。
	static
	{
	java.security.Security.addProvider(new com.isnetworks.provider.jce.ISNetworksProvider());
    }
    /**
     *对XML文档进行数字签名。
     */
	public static void sign(Document doc, String keystore, String storetype,
						String storepass, String alias, String keypass) throws Exception {
		FileInputStream fileInputStream = new FileInputStream(keystore);
		java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
		keyStore.load(fileInputStream, storepass.toCharArray());
		PrivateKey key = (PrivateKey)keyStore.getKey(alias, keypass.toCharArray());
		X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);

		SigningKey sk = SigningKeyFactory.makeSigningKey(key);
		KeyInfo ki = new KeyInfo();
		ki.setCertificate(cert);

		WSSecurity wSSecurity = new WSSecurity();
		wSSecurity.sign(doc, sk, ki);//签名。
	}
    /**
     *对XML文档进行身份验证。
     */
	public static boolean verify(Document doc, String keystore, String storetype,
						String storepass) throws Exception {
		FileInputStream fileInputStream = new FileInputStream(keystore);
		java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
		keyStore.load(fileInputStream, storepass.toCharArray());

		TrustVerifier verifier = new X509TrustVerifier(keyStore);

		WSSecurity wSSecurity = new WSSecurity();
		MessageValidity[] resa = wSSecurity.verify(doc, verifier, null,null);
		if (resa.length &gt; 0)
			return resa[0].isValid();
		return false;
	}
   /**
    *对XML文档进行加密。必须有JSSE提供者才能加密。
    */
	public static void encrypt(Document doc, String keystore, String storetype,
						String storepass, String alias) throws Exception {
		try
		{
				
		FileInputStream fileInputStream = new FileInputStream(keystore);
		java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
		keyStore.load(fileInputStream, storepass.toCharArray());
		X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
		
		PublicKey pubk = cert.getPublicKey();
		KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede",PROVIDER);		
		keyGenerator.init(168, new SecureRandom());
		SecretKey key = keyGenerator.generateKey();
		KeyInfo ki = new KeyInfo();
		ki.setCertificate(cert);

		WSSecurity wSSecurity = new WSSecurity();
		//加密。
		wSSecurity.encrypt(doc, key, AlgorithmType.TRIPLEDES, pubk, AlgorithmType.RSA1_5, ki);
	}
	catch(Exception e)
	{
		e.printStackTrace();
	}
	}
    /**
     *对文档进行解密。
     */
	public static void decrypt(Document doc, String keystore, String storetype,
						String storepass, String alias, String keypass) throws Exception {
		FileInputStream fileInputStream = new FileInputStream(keystore);
		java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
		keyStore.load(fileInputStream, storepass.toCharArray());
		PrivateKey prvk2 = (PrivateKey)keyStore.getKey(alias, keypass.toCharArray());

		WSSecurity wSSecurity = new WSSecurity();
		//解密。
		wSSecurity.decrypt(doc, prvk2, null);
		WsUtils.removeEncryptedKey(doc);//从 WS-Security Header中删除 EncryptedKey 元素
	}

	public static void removeWSSElements(Document doc) throws Exception {
		WsUtils.removeWSSElements(doc);// 删除WSS相关的元素。
	}		
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>WSSHelper类中使用了ISNetworks安全提供者，ISNetworks实现了RSA加密、解密算法。当然，你也可以使用其它的安全提供者，并且可以使用不同的加密算法。可以从网络上下载ISNetworks相关包。</P>
<P>WSSHelper中包含了一个WsUtils类，它的功能就是从加密后的SOAP消息中删除一些WS-Security元素，删除这些元素后的SOAP消息才能被最终的客户端或者Web服务端处理。</P>
<P><A name=N101BB><SPAN class=atitle3>服务器端Handler开发</SPAN></A><BR>当请求到达后，服务端Handler调用handleRequest方法，执行如下过程：对请求SOAP消息解密'身份验证'删除WSS元素'把Document转换成SOAP消息。 Web服务端点对请求做出响应后，将调用handleResponse方法，执行如下过程：对响应的SOAP消息进行数字签名'加密'把Document转换成SOAP消息。</P><A name=N101C4><B>例程8 服务器端Handler（WSSecurityServerHandler.java）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;
...

//服务器端Handler
public class WSSecurityServerHandler implements Handler 
{
	 	//密匙库相关信息
private String keyStoreFile = null;
		private String keyStoreType = "JKS";
		。。。
		
		public WSSecurityServerHandler()
		{
			System.out.println("服务端Handler：构造方法");
		}
	    /**
	     *处理请求
	     *流程：解密--&gt;身份验证--&gt;删除WSS元素'把Document转换成SOAP消息。
	     */	     
	    public boolean handleRequest(MessageContext messageContext) {
	 	
	        	System.out.println("开始处理请求。。。");
    	if (messageContext instanceof SOAPMessageContext){
			try {
	SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
				SOAPMessage soapMessage = soapMessageContext.getMessage();
				soapMessage.writeTo(System.out);
	Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
                    //解密
				WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
						keyStorePassword, keyAlias, keyEntryPassword);
                     //身份验证
				WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
				//删除WSS元素
WSSHelper.removeWSSElements(doc);

				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
				soapMessageContext.setMessage(soapMessage);
			} catch (Exception e){

				System.err.println("在处理请求时发生了异常： " + e);
				e.printStackTrace();
				return false;
			}
		} else {
			System.out.println("MessageContext是以下类的实例： " + messageContext.getClass());
		}
		 System.out.println("处理请求完毕！");
        return true;
    }
    
    /**
     *处理响应
     *流程：数字签名--&gt;加密--&gt;把Document转换成SOAP消息。
     */
     public boolean handleResponse(MessageContext messageContext) {
        	System.out.println("开始处理Web服务响应。。。");
        if (messageContext instanceof SOAPMessageContext){
			try {
		SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
				SOAPMessage soapMessage = soapMessageContext.getMessage();
		Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
				WSSHelper.sign(doc, keyStoreFile, keyStoreType,
						keyStorePassword, keyAlias, keyEntryPassword);
				WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
						trustStorePassword, certAlias);

				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
				soapMessageContext.setMessage(soapMessage);
			} catch (Exception e){
				System.err.println("在处理响应时发生以下错误： " + e);
				e.printStackTrace();
				return false;
			}
		}
	
		System.out.println("处理响应完毕！");
        return true;
    }
    /**
     *初始化，主要是初始化一些相关参数。
     */
     public void init(HandlerInfo config) {
		System.out.println("WSSecurityServerHandler初始化");
		Object param = "";
		Map configs = config.getHandlerConfig();
		keyStoreFile = (String)configs.get("keyStoreFile");
		trustStoreFile = (String)configs.get("trustStoreFile");
		…//其它参数初始化	
    }
	…
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101CE><SPAN class=atitle3>客户端Handler开发</SPAN></A><BR>客户端Handler可以是任何JAX-RPC兼容的Handler处理器。比如AXIS Handler实现或者SUN 提供的JAX-RPC Handler参考实现。这里使用后者来作为客户端Handler处理器。</P>
<P>客户端Handler和服务器端Handler原理一样，但处理过程完全相反。</P><A name=N101DA><B>例程9 客户端Handler（WSSecurityClientHandler.java）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;
…

//客户端Handler
public class WSSecurityClientHandler implements Handler 
{       
       //密匙库相关信息   
	    ...
		
	    /**
	     *处理请求
	     *流程：数字签名--&gt;加密--&gt;把Document转换成SOAP消息。
	     */	     
	    public boolean handleRequest(MessageContext messageContext) {
	 	
	        	System.out.println("开始处理请求。。。");
           …
		       	WSSHelper.sign(doc, keyStoreFile, keyStoreType,
				keyStorePassword, keyAlias, keyEntryPassword);
			    WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
trustStorePassword, certAlias);
				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
				soapMessageContext.setMessage(soapMessage);
			…
		 System.out.println("处理请求完毕！");
        return true;
    }
    
    /**
     *处理响应
     *流程：解密--&gt;身份验证--&gt;删除WSS元素'把Document转换成SOAP消息。
     */
     public boolean handleResponse(MessageContext messageContext) {

        	System.out.println("开始处理Web服务响应。。。");
       …
			    WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
						keyStorePassword, keyAlias, keyEntryPassword);				
				
				WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
				WSSHelper.removeWSSElements(doc);

				
				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
								System.out.println("the final message is:");
								soapMessage.writeTo(System.out);
				soapMessageContext.setMessage(soapMessage);
			…		
			System.out.println("处理响应完毕！");
        return true;
    }
    /**
     *初始化，主要是初始化一些相关参数。
     */
     public void init(HandlerInfo config) {
		…
    }
	…
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N101E4><SPAN class=atitle3>部署服务器端Handler</SPAN></A><BR>为了使用Handler，需要在Web服务部署描述符中指定使用此Handler。Handler包含的初始化参数也在此描述，如例程10所示。</P><A name=N101ED><B>例程10 服务器端Handler部署代码</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
  &lt;service name="PersonalTaxServicePort" provider="java:RPC"&gt;
  &lt;parameter name="allowedMethods" value="*"/&gt;
  &lt;parameter name="className" value="com.hellking.study.webservice.PersonalTaxService"/&gt;
  &lt;parameter name="wsdlTargetNamespace" value="http://hellking.webservices.com/"/&gt;
  &lt;parameter name="wsdlServiceElement" value="PersonalTaxService"/&gt;
  &lt;parameter name="wsdlServicePort" value="PersonalTaxServicePort"/&gt;
  &lt;parameter name="wsdlPortType" value="PersonalTaxService"/&gt;
  &lt;requestFlow&gt;
   &lt;handler type="java:org.apache.axis.handlers.JAXRPCHandler"&gt;
    &lt;parameter name="scope" value="session"/&gt;
    &lt;parameter name="className"
            value="com.hellking.study.webservice.WSSecurityServerHandler"/&gt;
    &lt;parameter name="keyStoreFile"
            value="K:\\jakarta-tomcat-5.0.16\\server.keystore"/&gt;
    &lt;parameter name="trustStoreFile"
            value="K:\\jakarta-tomcat-5.0.16\\server.truststore"/&gt;
    &lt;parameter name="certAlias" value="clientkey"/&gt;
   &lt;/handler&gt;
  &lt;/requestFlow&gt;
  &lt;responseFlow&gt;
   &lt;handler type="java:org.apache.axis.handlers.JAXRPCHandler"&gt;
    &lt;parameter name="scope" value="session"/&gt;
    &lt;parameter name="className"
            value="com.hellking.study.webservice.WSSecurityServerHandler"/&gt;
    &lt;parameter name="keyStoreFile"
            value="K:\\jakarta-tomcat-5.0.16\\server.keystore"/&gt;

    &lt;parameter name="trustStoreFile"
            value="K:\\jakarta-tomcat-5.0.16\\server.truststore"/&gt;
    &lt;parameter name="certAlias" value="clientkey"/&gt;
   &lt;/handler&gt;
  &lt;/responseFlow&gt;
  &lt;/service&gt;
  </CODE></PRE></TD></TR></TBODY></TABLE>
<P>requestFlow表示Web服务PersonalTaxServicePort的请求处理Handler链。这里只有一个Handler，就是WSSecurityServerHandler。当Web服务请求到达PersonalTaxServicePort时，WSSecurityServerHandler的handleRequest方法将被自动调用。</P>
<P>注意：部署时，请改变Handler相关参数以和目标的Web服务一致，比如trustStoreFile的路径等。</P>
<P><A name=N101FD><SPAN class=atitle3>调用测试</SPAN></A><BR>这里采用代理的方式来调用Web服务，先编写一个Web服务接口。</P><A name=N10206><B>例程11 TaxServiceInterface</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;
…
/**
 *个人所得税Web服务。
 */
public interface TaxServiceInterface extends Remote
{
	public double getTax(double salary)throws java.rmi.RemoteException;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>WSSClient客户端程序是通过代理的方式来访问Web服务的。由于要使用Handler，所以在访问前通过registerHandlers（）方法注册了WSSecurityClientHandler，并且初始化了WSSecurityClientHandler的相关参数。当然，JAX-RPC"参考实现"还支持在Web服务客户端配置文件中描述Handler信息，这样就不需要在客户端代码中对Handler进行注册了，你可以参考相关文档。</P><A name=N10213><B>例程12 测试客户端程序（WSSClient）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;

...
/**
 *调用需要验证的Web服务
 */
public class WSSClient
{
	static	final double salary=5000;
    public static void main(String [] args)
    {
        try {
        	//服务端的url，需要根据情况更改。            
            String endpointURL = "http://localhost:8080/axis/services/PersonalTaxServicePort";
            String wsdlURL=endpointURL+"?wsdl";
            java.net.URL targetURL= new java.net.URL(wsdlURL);
            String nameSpaceUri = "http://hellking.webservices.com/";
            String svcName = "PersonalTaxService";		
		    String portName = "PersonalTaxServicePort";

            ServiceFactory svcFactory = ServiceFactory.newInstance();

            Service svc = svcFactory.createService(targetURL, new QName(nameSpaceUri, svcName));
            //cfg表示客户端的配置信息。
            java.util.HashMap cfg = new java.util.HashMap();
			
			cfg.put("keyStoreFile", "client.keystore");			
			cfg.put("trustStoreFile", "client.truststore");			
			cfg.put("certAlias", "changeit");
			
		    Class hdlrClass = com.hellking.study.webservice.WSSecurityClientHandler.class;
			
			java.util.List list = svc.getHandlerRegistry().
			
			               getHandlerChain(new QName(nameSpaceUri, portName));
			
			list.add(new javax.xml.rpc.handler.HandlerInfo(hdlrClass, cfg, null));
			
		     registerHandlers (svc);
			
			 TaxServiceInterface myProxy = 
               ( TaxServiceInterface) svc.getPort(new QName(nameSpaceUri, portName), 
                 TaxServiceInterface.class);       
              double ret=myProxy.getTax(5000);
			System.out.println("使用HTTP协议来作为Web服务的传输协议！");			
            System.out.println("已经成功调用。请参看服务端的输出!");
            System.out.println("输入工资"+salary+"元，应交个人所得税："+ret);
        } catch (Exception e) {
        	e.printStackTrace();
        }
    }
    //注册Handler
    private static void registerHandlers ( Service service ) 
		throws javax.xml.rpc.ServiceException {
		
		java.util.HashMap cfg = new java.util.HashMap();
			
			cfg.put("keyStoreFile", "client.keystore");			
			cfg.put("trustStoreFile", "client.truststore");			
			cfg.put("certAlias", "changeit");
			
		/*
		* 封装客户端Handler到HandlerInfo 中，然后添加到Handler链中。
		*/ 
	javax.xml.rpc.handler.HandlerInfo info = new javax.xml.rpc.handler.HandlerInfo
	(com.hellking.study.webservice.WSSecurityClientHandler.class, cfg, null );
		java.util.ArrayList handlerList = new java.util.ArrayList();
		handlerList.add(info);
		
		/*
		* 获得Handler注册
		*/
		javax.xml.rpc.handler.HandlerRegistry handlerRegistry = service.getHandlerRegistry();
		
		/*
		* 把Handler添加到所有的port中。
		*/
		java.util.Iterator portIterator = service.getPorts();
		while ( portIterator.hasNext()) {
			Object obj=portIterator.next();
		QName portName = (QName) obj;
		handlerRegistry.setHandlerChain(portName, handlerList);
		}
    }
    
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意：由于客户端使用了SUN公司提供的"JAX-RPC参考实现"，所以必须把jaxrpc-impl.jar包设置在CLASSPATH环境变量中，并且不要把axis.jar设置在客户端CLASSPATH环境变量，否则会出现ClassCastException异常。这是因为axis也是JAX-RPC的实现，如果它在CLASSPATH环境变量中，当调用：</P>
<P>ServiceFactory svcFactory = ServiceFactory.newInstance()方法时，就可能初始化一个axis的ServiceFactory 实现。</P>
<P>本文源代码中client目录下wss-client.bat文件包含了执行WSSClient脚本，修改了部分环境变量参数后，才能执行。</P>
<P><A name=N10226><SPAN class=atitle2>总结</SPAN></A><BR>本文和上一篇文章介绍了几种不同实现Web服务安全的方法，你可以根据具体应用对安全的要求级别采用不同的方式。对于安全级别要求不高的应用，可以采用在Web服务器上使用基本认证、使用Axis的Handler或者使用Servlet过滤器来实现访问控制；对于安全要求高的应用，可以采用本篇介绍开发的"axis下实现WS-Security的通用应用框架"来实现安全性。</P><img src ="http://www.blogjava.net/Victor/aggbug/6673.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-06-24 15:08 <a href="http://www.blogjava.net/Victor/articles/6673.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现安全的AXIS Web服务，第1部分(转)</title><link>http://www.blogjava.net/Victor/articles/6672.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 24 Jun 2005 07:07:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/6672.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/6672.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/6672.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/6672.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/6672.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR>
<TD width=8><IMG height=21 alt="" src="http://www.ibm.com/i/c.gif" width=5></TD>
<TD width=160>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=160 bgColor=#000000 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerworks/cn/i/bg-gold.gif height=5><B>内容：</B></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#N1006C">Web服务安全概述</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#N1009F">开发、部署示例Web服务</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#N100C9">从Web应用的基本认证说起</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#N10121">使用axis的Handler进行访问控制</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#N10189">使用SSL作为Web服务的传输协议</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#N1022D">总结、下一步</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR><!--Standard links for every dw-article-->
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#resources">参考资料 </A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#author1">关于作者</A></TD></TR>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#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></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=1 width=160 border=0>
<TBODY>
<TR>
<TD><IMG height=1 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>
<TR>
<TD height=1><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=150 bgColor=#000000 colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www.ibm.com/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#author1"><NAME>陈亚强</NAME></A><BR>高级软件工程师<BR>2004 年 8 月 </P>
<BLOCKQUOTE>本文首先简单介绍Web服务安全性基本概念，然后介绍在axis Web服务开发工具下处理安全性问题的一些常用方法；接下来以安全性实现方法为线索，介绍了怎么在Web应用的基础上配置Web服务的访问控制、怎么使用axis的Handler和Servlet的过滤器进行访问控制，最后讨论怎么在tomcat下配置SSL 以实现Web服务的安全传输。</BLOCKQUOTE>
<P>axis是流行的Web服务开发工具，如何在axis下开发安全的Web服务，这是摆在每个开发者面前的问题。本文是J2EE Web服务开发系列文章的第十二篇，将首先简单介绍Web服务安全性基本概念，然后介绍在axis Web服务开发工具下处理安全性问题的一些常用方法；接下来以安全性实现方法为线索，介绍了怎么在Web应用的基础上配置Web服务的访问控制、怎么使用axis的Handler和Servlet的过滤器进行访问控制，最后讨论怎么在tomcat下配置SSL 以实现Web服务的安全传输。本文下一篇将介绍怎么在axis中实现WS-Security。</P>
<P>阅读本文前您需要以下的知识和工具：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>Apache axis1.1，并且会初步使用； 
<LI>Tomcat 4.0以上, 并且会初步使用； 
<LI>Servlet的开发经验； 
<LI>SOAP消息（SOAP Message）编程知识； 
<LI>JAX-RPC编程基础知识； 
<LI>Java安全编程基础知识。 </LI></UL>
<P>本文的参考资料见 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">参考资料</A>。 </P>
<P>本文的全部代码在这里 <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/src.rar" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">下载</A>。 </P>
<P><A name=N1006C><SPAN class=atitle2>Web服务安全概述</SPAN></A><BR>安全的Web服务是Web服务成功的必要保证。但众所周知的是，Web服务使用XML来进行数据交换，而XML在默认情况下是明文编码的；同时，大部分Web服务使用HTTP协议作为传输协议，同样，HTTP也是使用明文方式来传输数据的。这就造成了在不加密的传输协议上传输不加密的信息，从而使信息传输的保密性受到威胁。作为企业级的应用，以上的方式不能满足安全性基本要求：</P>
<OL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>数据在因特网上传播时不应该被第三方看到； 
<LI>双方必须能够确定消息的来源； 
<LI>双方必须能够确定被传送的数据没有被篡改。 </LI></OL>
<P>通过使用SSL协议可以解决第一个问题："不应该被第三方看到"；使用数字签名和数字证书可以解决后面两个问题。当使用数字证书方法时，Web 服务请求者必须有一个由可信认证中心签署的数字证书。请求者使用这个证书来表明它们的身份，并对 SOAP 消息进行数字签名。对方系统接收到消息后，就可对消息做时间戳记并进行日志记录。此时，数字签名会得到验证。验证过程将确保消息来自发送方，并且还要验证消息内容在传输过程中没有被篡改。</P>
<P>IBM、Microsoft 和 Verisign 于2002年十二月份联合发布了一个关于 Web 服务安全性（Web Services Security，WS-Security）的规范，该规范描述如何向 SOAP 消息附加签名和加密报头；另外，它还描述如何向消息附加安全性令牌（包括二进制安全性令牌，如 X.509 证书），提供了一套帮助 Web 服务开发者保护 SOAP 消息交换的机制。</P>
<P>根据应用的对安全要求的级别不同，可以采用不同的方式来实现安全性，以下是目前最常用的一些实现方式（从低到高排列）：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>J2EE Web应用默认的访问控制（数据是明文的）； 
<LI>使用axis的Handler进行访问控制（数据是明文的）； 
<LI>使用Servlet过滤器（Filter）进行访问控制（数据是明文的）； 
<LI>使用SSL/HTTPS协议来传输（加密的数据传输协议）； 
<LI>使用WS-Security规范对信息进行加密与身份认证（数据被加密传输）。 </LI></UL>
<P>前三种方式对于安全级别要求不高的应用是可行的，它能够使用Web应用访问认证机制来进行权限验证，从而保护对资源的访问。但需要注意的是，虽然它们进行了身份验证，但信息的传递还是以明文的方式进行的，不能保证信息在传输过程中不被窃取。SSL是一个安全的传输协议，使用它传输Web服务能保证信息不被第三方窃取。但它有个缺点就是对系统资源消耗大。采用最后一种方式，信息被签名后再加密，然后把加密后的信息网络上传播，这样，即使第三方获得加密后的传输信息，也不能解密。对于安全级别要求高的系统，应该采用WS-Security规范来作为Web服务安全性解决方案。</P>
<P><A name=N1009F><SPAN class=atitle2>开发、部署示例Web服务</SPAN></A><BR>作为一个实例性的教程，我们有必要先开发一个简单的Web服务作为示例。关于如何在Axis下开发并部署一个简单的Web服务你可以参考下面这篇文章： <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-jax-rpc/part1/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">用JAX-RPC开发Web服务：Servlet作为Web服务端点</A>。 </P>
<P>下面的例子提供的是一个提供个人所得税计算的Web服务。客户端传入工资的金额，Web服务将计算个人所得税金额。</P><A name=N100AF><B>例程 1 个人所得税Web服务</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;
public class PersonalTaxService
{
	final double base=1200;//所得税上缴基数。	
	public  double getTax(double salary)
    {
    	double tax_salary=salary-1200;    	
    	double tax=0.0d;//计算后的所得税。    	
    	if(0&gt;tax_salary)                             tax=0;
    	else if(0&lt;tax_salary&amp;&amp;tax_salary &lt;=500)      tax=tax_salary*0.05-0;
    	else if(500&lt;tax_salary&amp;&amp;tax_salary&lt;=2000)    tax=tax_salary*0.10-25;
     …    	   	
    	return tax;
    } 
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>编译后，你只要在Axis的部署配置文件（AXIS_HOME\Web-INF\server-config.wsdd）的适当位置增加以下部署代码，Web服务就可以部署成功。</P><A name=N100BC><B>例程2 部署示例Web服务</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
    &lt;service name="PersonalTaxService" provider="java:RPC"&gt;
  &lt;parameter name="allowedMethods" value="*"/&gt;
  &lt;parameter name="className" value="com.hellking.study.webservice.PersonalTaxService"/&gt;
  &lt;/service&gt;
  </CODE></PRE></TD></TR></TBODY></TABLE>
<P>部署后的Web服务URL为： http://localhost:8080/axis/services/PersonalTaxService?wsdl （请先部署好这个Web服务再进行下面的学习。）</P>
<P><A name=N100C9><SPAN class=atitle2>从Web应用的基本认证说起</SPAN></A><BR>不管客户端通过什么API来调用Web服务，它总是先构造SOAP消息，然后通过HTTP POST方法把消息发送到Web服务的URL（注1：当然，Web服务也可以通过其它基于文本的协议传输，但是由于非常少见，在这里不讨论）。而我们知道，不管axis部署在哪种服务器上（Tomat还是Websphere），它总是以Servlet方式来运行的。所以最简单实现Web服务安全的方式就是通过在web应用的配置文件（web.xml）来实现访问控制。</P>
<P>J2EE web应用中有多种认证方式：BASIC（基本认证方法）、FORM（基于表单）、DIGEST（消息摘要）和CLIENT-CERT（数字证书）。关于J2EE Web应用的其它认证方式的配置请参考其它资料，在此不再介绍。</P>
<P>下面以Tomcat使用服务器为例，使用Web应用模型中基本认证方法来配置Web服务的安全访问。</P>
<P>Web应用的基本认证是建立在J2EE角色和用户的基础之上，首先在Tomcat角色配置文件中增加一个角色和一个帐号。</P><A name=N100DB><B>例程3 在tomcat-users.xml中增加角色（Tomcat_Home\conf\tomcat-users.xml）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;tomcat-users&gt;
…  
&lt;role rolename="department-manager"/&gt;
  &lt;user username="hellking" password="simplewebservices" roles="department-manager"/&gt; 
…
&lt;/tomcat-users&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>上面配置代码在tomcat配置文件中添加了一个department-manager角色，并且在此角色中添加了一个名为hellking的用户。要使tomcat-users.xml中配置的角色和用户生效，需要配置tomcat使用UserDatabaseRealm。打开Tomcat_Home\conf\server.xml配置文件，在GlobalNamingResources中添加以下描述：</P><A name=N100E8><B>例程4 在tomcat中添加UserDatabaseRealm</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
  &lt;GlobalNamingResources&gt;
...
  &lt;Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
       description="User database that can be updated and saved"&gt;
    &lt;/Resource&gt;
    &lt;ResourceParams name="UserDatabase"&gt;
      &lt;parameter&gt;
        &lt;name&gt;factory&lt;/name&gt;
        &lt;value&gt;org.apache.catalina.users.MemoryUserDatabaseFactory&lt;/value&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;name&gt;pathname&lt;/name&gt;
        &lt;value&gt;conf/tomcat-users.xml&lt;/value&gt;
      &lt;/parameter&gt;
    &lt;/ResourceParams&gt;
  &lt;/GlobalNamingResources&gt;
  </CODE></PRE></TD></TR></TBODY></TABLE>
<P>然后再web应用的部署描述符中指定Web服务资源的访问控制，如下所示：</P><A name=N100F5><B>例程5 配置对Servlet的访问控制（AXIS_HOME\WEB-INF\web.xml）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;security-constraint&gt;
  &lt;web-resource-collection&gt;
    &lt;web-resource-name&gt;Tax Web service &lt;/web-resource-name&gt;
    &lt;url-pattern&gt;/services/PersonalTaxService&lt;/url-pattern&gt;
  &lt;/web-resource-collection&gt;
  &lt;auth-constraint&gt;
    &lt;role-name&gt;department-manager&lt;/role-name&gt;
  &lt;/auth-constraint&gt;
&lt;/security-constraint&gt;
&lt;login-config&gt;
  &lt;auth-method&gt;BASIC&lt;/auth-method&gt;
  &lt;realm-name&gt;Axis Basic Authentication Area&lt;/realm-name&gt;
&lt;/login-config&gt;
&lt;security-role&gt;
  &lt;role-name&gt;department-manager&lt;/role-name&gt;
&lt;/security-role&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>url-pattern指定了需要通过角色验证的URL样式，在这里是"/services/PersonalTaxService"；role-name是能够访问制定URL的角色，这里是department-manager。以上配置的意思是只有角色类型是"department-manager"的用户才能访问URL样式为"/services/PersonalTaxService"Web服务。</P>
<P>下面我们看怎么在axis客户端代码中axis客户端，如例程6所示。</P><A name=N10105><B>例程6 axis客户端代码</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
。。。

/**
 *调用需要验证的Web服务
 */
public class AuthClient
{
	static	final double salary=5000;
    public static void main(String [] args)
    {
        try {
        	//服务端的url，需要根据情况更改。            
            String endpointURL = "http://localhost:8080/axis/services/PersonalTaxService?wsdl";//
// Web服务端点地址            
            Service  service = new Service();
            Call     call    = (Call) service.createCall();
            call.setTargetEndpointAddress( new java.net.URL(endpointURL) );
            call.setOperationName( new QName("PersonalTaxService", "getTax") );//设置操作的名称。
            //由于需要认证，故需要设置调用的用户名和密码。
            call.getMessageContext().setUsername("hellking");//设置用户名。
            call.getMessageContext().setPassword("simplewebservices");//设置密码
            call.addParameter( "op1", XMLType.XSD_DOUBLE, ParameterMode.IN );//参数的类型
		   call.setReturnType( XMLType.XSD_DOUBLE );//返回的数据类型
            Double ret = (Double) call.invoke( new Object [] { new Double(salary) });//执行调用
		  System.out.println("使用HTTP协议来作为Web服务的传输协议！");			
            System.out.println("已经成功调用。请参看服务端的输出!");
            System.out.println("输入工资"+salary+"元，应交个人所得税："+ret);
     。。。
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>可以看出，通过使用call.getMessageContext().setUsername("hellking")来设置调用的用户名，通过setPassword来设置调用的密码。通过上面的代码可以看出，除了为Call对象设置的用户名和密码外，其它的代码和不使用访问控制时一样。</P>
<P>下面我们看看它的运行结果：</P><A name=N10115><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
使用HTTP协议来作为Web服务的传输协议！
已经成功调用。请参看服务端的输出!
输入工资5000.0元，应交个人所得税：445.0
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>从输出可以看到，客户端成功通过了验证，并且返回了正确的结果。</P>
<P><A name=N10121><SPAN class=atitle2>使用axis的Handler进行访问控制</SPAN></A><BR>axis为Web服务的访问控制提供了相关的配置描述符，并且提供了一个访问控制的简单Handler（关于Handler的详细介绍见" <A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-handler/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">J2EE Web服务开发系列之六: 使用Handler来增强Web服务的功能</A>"）。默认情况下，你只要在配置描述符中添加用户，然后在Web服务器的部署描述符中自动允许的角色即可。 </P>
<P>首先在axis的配置文件users.lst（位于WEB-INF目录下）中添加一个用户，如"axisuser pass"，表示用户名为axisuser，密码为pass。然后把案例的Web服务重新部署，在server-config.wsdd中添加例程7所示的部署代码。</P><A name=N10131><B>例程7 重新部署PersonalTaxService</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
  &lt;service name="PersonalTaxService2" provider="java:RPC"&gt;
  &lt;parameter name="allowedMethods" value="*"/&gt;
    &lt;parameter name="className" value="com.hellking.study.webservice.PersonalTaxService"/&gt;
      &lt;parameter name="allowedRoles" value="axisuser"/&gt;
    &lt;requestFlow&gt;
    &lt;handler name="authen"
 type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/&gt;
    &lt;/requestFlow&gt;
  &lt;/service&gt;
  </CODE></PRE></TD></TR></TBODY></TABLE>
<P>在这个部署描述符中，指定PersonalTaxService2服务只能被axisuser访问，要想使访问控制生效，还必须把SimpleAuthenticationHandler添加到请求Handler链中。</P>
<P>你只要修改AuthClient代码的服务端点URL和访问用户名、密码，就可以测试新的Web服务了，如例程8所示。</P><A name=N10141><B>例程8 HandlerAuthClient</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public class HandlerAuthClient
{
。。。  
String endpointURL = "http://localhost:8080/axis/services/PersonalTaxService2?wsdl";
      。。。
   call.getMessageContext().setUsername("axisuser");//axis中的用户名。
   call.getMessageContext().setPassword("pass");//密码
。。。
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>执行HandlerAuthClient，能够顺利访问Web服务；如果修改用户名或者密码，那么就不能访问，这说明Axis的Handler对Web服务的访问权限进行了有效的控制。</P>
<P>使用Servlet过滤器（Filter）进行访问控制 Axis的Web服务端本质上是以Servlet方式在运行，所有我们完全可以在Web应用上部署一个Servlet过滤器，通过此过滤器来达到访问控制的效果。</P>
<P>Web应用中的过滤器截取从客户端进来的请求，然后进行一系列处理，最后把请求发送到目标Servlet。过滤器的工作原理如下图所示。</P>
<P><A name=N10156><B>图1 过滤器工作原理</B></A><BR><IMG height=452 alt="图1 过滤器工作原理" src="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/1.gif" width=671 border=0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P>过滤器可以说是外部进入Web服务器的第一道关，它能决定请求是否继续向前转发，也能对请求中的信息进行处理。如果过滤器用于对Web服务进行访问控制，那么它能根据客户端信息决定目标的服务是否能调用成功。</P>
<P>将要开发的过滤器将根据客户端IP地址进行过滤，如果客户端的IP地址在限制范围中，那么就不能访问目标的Web服务。过滤器部分代码如下。</P><A name=N1016C><B>例程9 用过滤器限制Web服务的访问权限</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;

import javax.servlet.FilterChain;
。。。
public class WebServicesFilter implements Filter
{
     //没有权限访问的IP地址
	static final String[] deniedIPList=new String[]{
		               "123.201",
		               "192.168",
		               "25.46",
		               "124.0.0.1"
		            };	
	 public boolean isIPDenied(String ipAddr)
     {
     	…
     }
	
	//过滤处理的方法
	public void doFilter(final ServletRequest req,final ServletResponse res,FilterChain chain)
		throws IOException,ServletException
	{
		 HttpServletRequest hreq = (HttpServletRequest)req;
         HttpServletResponse hres = (HttpServletResponse)res;
         //HttpSession session = hreq.getSession();
         String clientIp=req.getRemoteAddr();
         System.out.println("开始过滤。。。");
         if(isIPDenied(clientIp))
		 {
		 	 //验证不成功，让用户登录。
		 	throw new ServletException("无权限访问此Web服务！");
		  }		
		 else
			 {
			 	//验证成功，继续处理
			   chain.doFilter(req,res);		 	

			 }	 
		 	
	}	
	…
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>WebServicesFilter 过滤器限制了deniedIPList中指定的所有客户端。编写了过滤器后，需要在Web应用的部署描述符中指定使用此过滤器，并且把过滤器映射到目标URL上。当然，你也可以开发其它过滤器进行访问控制，比如Web服务客户端登录时，把登录信息保存在HTTP会话中，当客户端访问受限的资源时，读取HTTP会话中客户端信息以决定客户端是否有权限访问目标资源。</P>
<P>除了编写Servlet过滤器实现类外，还需要在web.xml中对它进行配置，并且把过滤器映射到要过滤的目标URI上。以下是过滤器的部署描述符。</P><A name=N1017C><B>例程10 部署过滤器</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
    &lt;filter&gt;
        &lt;filter-name&gt;WebServicesFilter&lt;/filter-name&gt;
        &lt;filter-class&gt;com.hellking.study.webservice.WebServicesFilter&lt;/filter-class&gt;
    &lt;/filter&gt;    
    &lt;filter-mapping&gt;
        &lt;filter-name&gt;WebServicesFilter&lt;/filter-name&gt;
        &lt;url-pattern&gt;/services/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;
    </CODE></PRE></TD></TR></TBODY></TABLE>
<P>url-pattern指定了过滤器要过滤的范围。"/services/*"表示以"/services"开始的URL将全部被过滤，这正是AxisServlet默认的URL。通过上面的配置，只要客户端调用axis Web服务，就会被WebServicesFilter过滤器过滤。如果客户端IP地址在过滤器的deniedIPList中，那么就不能访问目标服务。</P>
<P><A name=N10189><SPAN class=atitle2>使用SSL作为Web服务的传输协议</SPAN></A><BR>Web服务也可以使用SSL作为传输协议。虽然JAX-RPC并没有强制规定是否使用SSL协议，但在tomcat下使用HTTPS协议。</P>
<P><A name=N10192><SPAN class=atitle3>相关知识：</SPAN></A><BR>SSL由两个共同工作的协议组成："SSL 记录协议"（SSL Record Protocol）和"SSL 握手协议"（SSL Handshake Protocol）。SSL 记录协议建立在可靠的传输协议（如TCP）之上，为高层协议提供数据封装、压缩、加密等基本功能的支持；SSL 握手协议建立在SSL记录协议之上，用于在实际的数据传输开始前，通信双方进行身份认证、协商加密算法、交换加密密钥等。</P>
<P>SSL握手协议包含两个阶段，第一个阶段用于建立私密性通信信道，第二个阶段用于客户认证。第一阶段是通信的初始化阶段，在此阶段，首先SSL要求服务器向浏览器出示证书；然后浏览器中的SSL软件发给服务器一个随机产生的传输密钥，此密钥由已验证过的公钥加密，随机产生的传输密钥是核心机密，只有客户的浏览器和此公司的Web服务器知道这个数字序列。第二阶段的主要任务是对客户进行认证，此时服务器已经被认证了。服务器方向客户发出认证请求消息。客户收到服务器方的认证请求消息后，发出自己的证书，并且监听对方回送的认证结果。而当服务器收到客户的证书后，给客户回送认证成功消息，否则返回错误消息。到此为止，握手协议全部结束。</P>
<P>要使用SSL协议，服务器至少有一个私有密匙和一个用于验证身份的证书。私有密匙在密匙交换算法中用到，证书将发送到客户端，以通知服务器端的身份。如果SSL服务器要验证客户端的身份，那么客户端必须也有自己的密匙库（包含私有密匙和证书）。JSSE中引入了信任库（truststore）的概念，它是用来保存证书的数据库。客户端或者服务器通过信任库来验证对方的身份。</P>
<P>在使用SSL前，必须确保系统安装了JSSE。JDK1.4版本默认以及安装了JSSE。如果没有安装，把下载安装好的jar文件拷贝到%JAVA_HOME%\ jre\lib\ext目录下。这样，就安装好了JSSE的运行环境。</P>
<P>下面我们使用JDK自带的工具创建密匙库和信任库。</P>
<P>1）通过使用一下的命令来创建服务器端的密匙库。</P><A name=N101AA><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
        keytool -genkey -alias hellking -keystore server.keystore -keyalg RSA
输入keystore密码：  changeit
您的名字与姓氏是什么？
  [Unknown]：  hellking-Server
您的组织单位名称是什么？
  [Unknown]：  huayuan
您的组织名称是什么？
  [Unknown]：  huayuan
您所在的城市或区域名称是什么？
  [Unknown]：  beijing
您所在的州或省份名称是什么？
  [Unknown]：  beijing
该单位的两字母国家代码是什么
  [Unknown]：  cn
CN=chen ya qiang, OU=huayuan, O=huayuan, L=beijing, ST=beijing, C=cn 正确吗？
  [否]：  y

输入&lt;hellking&gt;的主密码
        （如果和 keystore 密码相同，按回车）：
        </CODE></PRE></TD></TR></TBODY></TABLE>
<P>以上命令执行完成后，将获得一个名为server.keystore的密匙库。</P>
<P>2)生成客户端的信任库。首先输出RSA证书：</P><A name=N101B9><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
keytool -export -file test_axis.cer -storepass changeit -keystore server.keystore
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>然后把RSA证书输入到一个新的信任库文件中。这个信任库被客户端使用，被用来验证服务器端的身份。</P><A name=N101C5><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
keytool -import -file test_axis.cer -storepass changeit -keystore client.truststore -alias serverkey -noprompt
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>3）创建客户端密匙库。重复步骤1，创建客户端的密匙库。也可以使用以下命令来完成：</P><A name=N101D1><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
keytool -genkey -dname " CN=hellking-Client, OU=tsinghua, O=tsinghua, L=BEIJING, S=BEIJING, C=CN"
-storepass changeit -keystore client.keystore -keyalg RSA -keypass changeit
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>4）生成服务器端的信任库。</P><A name=N101DD><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
keytool -export -file test_axis.cer -storepass changeit -keystore client.keystore
keytool -import -file test_axis.cer -storepass changeit -keystore server.truststore -alias clientkey -noprompt
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>生成了密匙库和信任库，我们把服务器端的密匙库（server.keystore）和信任库（server.truststore）拷贝到Tomcat的某个目录。</P>
<P>下面需要更改Tomcat的配置文件（server.xml），增加一下部署描述符：</P><A name=N101EC><B>例程11 为Tomcat配置SSL协议。</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
                  &lt;Connector port="8443" 
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" debug="0" scheme="https" secure="true"
               clientAuth="true" keystoreFile="K:\jakarta-tomcat-5.0.16\server.keystore" keystorePass="changeit"
                 truststoreFile="K:\jakarta-tomcat-5.0.16\server.truststore" truststorePass="changeit"
               sslProtocol="TLS" /&gt;
               </CODE></PRE></TD></TR></TBODY></TABLE>
<P>clientAuth参数制定服务器是否要验证客户端证书，如果指定为true，那么客户端必须拥护服务器端可信任的证书后服务器才能响应客户端；如果指定为false，那么服务器不需要验证客户端的证书。</P>
<P>在此，我们又把PersonalTaxService部署一次，在server-config.wsdd中添加如下部署代码。</P><A name=N101FC><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;service name="PersonalTaxService3" provider="java:RPC"&gt;
  &lt;parameter name="allowedMethods" value="*"/&gt;
  &lt;parameter name="className" value="com.hellking.study.webservice.PersonalTaxService"/&gt;
  &lt;/service&gt;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>最后我们需要修改客户端调用程序，如例程12所示。</P><A name=N10208><B>例程12 SSL客户端调用程序</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.hellking.study.webservice;
….
public class SSLAuthClient
{
	static	final double salary=5000;
    public static void main(String [] args)
    {
        try {
        	//服务端的url，注意使用了SSL协议后，前缀是https。 
           String endpointURL = "https://localhost:8443/axis/services/PersonalTaxService3?wsdl";
           ….
            //由于使用了证书数字证书，所以不使用用户名和密码验证。
//call.getMessageContext().setUsername("hellking");。
            //call.getMessageContext().setPassword("simplewebservices");
           ….
            Double ret = (Double) call.invoke( new Object [] { new Double(salary) });
			System.out.println("使用SSL协议来作为Web服务的传输协议！");		
             System.out.println("已经成功调用。请参看服务端的输出!");
             System.out.println("输入工资"+salary+"元，应交个人所得税："+ret);
        } catch (Exception e) {
        	e.printStackTrace();
        }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>最后使用一下的命令来执行客户端程序：</P><A name=N10215><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
set AXIS_LIB=K:\jakarta-tomcat-5.0.16\webapps\axis\WEB-INF\lib
SET CLASSPATH=.;%CLASSPATH%;%AXIS_LIB%\wsdl4j.jar.jar;%AXIS_LIB%\axis.jar;%AXIS_LIB%\jaxrpc.jar;%AXIS_LIB%
\saaj.jar;%AXIS_LIB%\commons-discovery.jar;%AXIS_LIB%\commons-logging.jar
java -Djavax.net.ssl.keyStore=client.keystore \
-Djavax.net.ssl.keyStorePassword=changeit \
-Djavax.net.ssl.trustStore=client.truststore \
com.hellking.study.webservice.SSLAuthClient
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>参数解释：通过-Djavax.net.ssl.keyStore来指定客户端密匙库，-Djavax.net.ssl.trustStore来指定客户端信任库。</P>
<P>最后的输出结果如下：</P><A name=N10224><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
使用SSL协议来作为Web服务的传输协议！
已经成功调用。请参看服务端的输出!
输入工资5000.0元，应交个人所得税：445.0
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=N1022D><SPAN class=atitle2>总结、下一步</SPAN></A><BR>使用Web应用模型中基本认证方法来配置Web服务的安全访问以及使用axis的Handler、Servlet的过滤器来进行访问控制，它们都是一些轻量级控制Web服务安全解决方案，因为都是把未加密的信息在网络上传播。使用了SSL作为传输协议后，信息在网络上是加密传输的，但对资源消耗多，并且对客户端要求严格（必须支持SSL），不能满足Web服务访问的便利性。下一部分将介绍怎么在安全性要求高的环境下在axis开发工具里实现WS-Security。</P><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-secaxis1/src.rar" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">下载样例代码在此。</A> <img src ="http://www.blogjava.net/Victor/aggbug/6672.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-06-24 15:07 <a href="http://www.blogjava.net/Victor/articles/6672.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应用AXIS开始Web 服务之旅(转)</title><link>http://www.blogjava.net/Victor/articles/1475.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Thu, 24 Feb 2005 03:44:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/1475.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/1475.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/1475.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/1475.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/1475.html</trackback:ping><description><![CDATA[<BLOCKQUOTE><EM>本文介绍使用AXIS作为开发环境来体会Web服务的开发过程。</EM></BLOCKQUOTE>
<P><A name=1><SPAN class=atitle2>一． 介绍</SPAN></A><BR>本文并不是想介绍Web服务的原理、系统架构等，我们假设您已经了解了关于Web服务的一些基本的概念、原理等知识。本文主要是针对那些已经了解Web服务概念，但是还没有亲身体会Web服务所带来令人欢欣鼓舞的特征的开发人员。在此我们认为你已经具备了Java、XML等基础知识，如果你还有其他开发环境的经验例如VB、VC那是再好不过的了。 </P>
<P><SPAN class=atitle3>1．Web服务</SPAN><BR>虽然我们并不想详细讲述Web服务的体系结构，但是大概的介绍一下还是有必要的。Web服务是一种新型的Web应用程序。不同于其他Web应用程序，它是自适应、自我描述、模块化的应用程序，并可以跨越Web进行发布、定位以及调用。简单的Web服务可以提供例如天气预报或者航班信息的服务。一旦部署了Web服务，其他的应用程序就可以发现和调用所部署的服务。 </P>
<P><SPAN class=atitle3>2．AXIS项目</SPAN><BR>Axis框架来自 Apache 开放源代码组织，它是基于JAVA语言的最新的 SOAP 规范（SOAP 1.2）和 SOAP with Attachments 规范（来自 Apache Group ）的开放源代码实现。有很多流行的开发工具都使用AXIS作为其实现支持Web服务的功能，例如JBuilder以及著名的Eclipse J2EE插件Lomboz。AXIS的最新版本是1.1，可以从<A href="http://ws.apache.org/axis/index.html" target=_blank>http://ws.apache.org/axis/index.html</A>下载。下图是AXIS核心引擎的体系结构图： </P>
<P align=center><IMG src="http://www-900.ibm.com/developerWorks/cn/webservices/ws-startaxis/1_axis_engine.jpg"></P>
<P align=center><B>图1</B></P>
<P>整个AXIS项目包括以下几个部分：</P>
<OL>
<LI>消息流子系统<BR>消息流子系统提供了灵活的消息传递框架，这个消息传递框架包括处理程序、链、序列化程序和反序列化程序。处理程序是一个处理请求、响应和故障流的对象。处理程序可被组合在一起成为链，而且可以使用一个灵活的部署描述符来配置这些处理程序的顺序。 
<LI>传输框架子系统<BR>提供了一个传输框架，这个传输框架可以帮助您创建自己的可插式传输发送器和传输侦听器。 
<LI>数据编码子系统<BR>AXIS完全按照 XML Schema 规范提供各种数据类型的自动序列化，并且提供功能扩展接口来使用您自己定制的序列化器和反序列化器。 
<LI>其他<BR>AXIS完全支持 WSDL 以及日志记录、出错以及故障处理机制。它同时提供一些工具用来讲WSDL文档转换成客户端的调用框架以及根据类来产生WSDL定义文档。 </LI></OL>
<P>AXIS目前版本支持的标准是：W3C SOAP 1.1 和 1.2；WSDL 1.1；SAAJ 1.1（SUN公司：SOAP with Attachments API for Java）；JAX-RPC（SUN公司：Java API for XML-Based RPC）1.0。 </P>
<P>除了前面介绍的AXIS外，本文中还将会用到TOMCAT，这里不再另行介绍。另外为了演示Web服务真正与开发环境无关以及AXIS产生的是标准的、符合规范的Web服务，我们还将用到微软公司的SOAP TOOLKIT以及微软的开发环境VB和VC来做为Web服务的客户端。 </P>
<P><A name=2><SPAN class=atitle2>二. 环境搭建</SPAN></A><BR>由于AXIS本身是基于JAVA语言开发的项目，并且是以Web应用形式发布的，因此它运行时需要一个应用服务器作为支撑。为了方便我们这里选用的是Tomcat。由于AXIS本身需要用到处理XML信息的包，所以我们建议使用JDK1.4并安装Tomcat 4.1.24。下面是环境搭建步骤，读取根据自身情况进行安装。 </P>
<OL>
<LI>安装JDK1.4.1 
<LI>安装Tomcat 4.1.24到C:\Tomcat并验证安装是否成功 
<LI>下载AXIS项目打包文件axis-1_1.zip解压缩后将目录中的webapps目录下的axis子目录拷贝到C:\Tomcat\webapps下。 
<LI>验证AXIS的安装：重新启动Tomcat服务器后打开浏览器输入网址http://localhost:8080/axis 后应该出现如下图所示页面，点击链接"Validate"来验证Axis所需的几个JAVA包是否齐全。 </LI></OL>
<P align=center><IMG src="http://www-900.ibm.com/developerWorks/cn/webservices/ws-startaxis/2_axis_home.jpg"></P>
<P align=center><B>图2</B></P>
<P>点击超链接Validate后，AXIS会自动检查所需的每一个JAVA组件，这协组件分为：必需组件以及可选组件，必须保证所有必需组件都存在，如下图所示即为验证成功。 </P>
<P align=center><IMG src="http://www-900.ibm.com/developerWorks/cn/webservices/ws-startaxis/3_axis_verify.jpg"></P>
<P align=center><B>图3</B></P>
<P><A name=3><SPAN class=atitle2>三． Web Service服务端开发</SPAN></A><BR>经过了前两步之后我们就可以开始Web服务之旅了！大多数人在学习一种编程语言的第一步都是从Hello world程序开始的，我们也不例外。我们将提供这样一个Web服务，通过给它传入姓名，服务返回：你好[姓名]，欢迎来到Web服务的世界。这就是我们的需求。我们将马上根据AXIS的要求完成我们的需求，你就会发现原来Web服务可以这么简单！ </P>
<P>编写JAVA类Hello.java，内容如下：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
public class Hello{
	public String hello(String name){
		if(name==null)
			name = "";
		return "你好"+name+"，欢迎来到Web服务的世界！";
}
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>仅此而已，无需编译，将该文件改名为Hello.jws并拷贝到AXIS应用目录C:\Tomcat\webapps\axis下。 </P>
<P>下面我们就可以测试该Web服务了，打开浏览器并输入刚刚创建的文件名对应的URL地址<A href="http://localhost:8080/axis/Hello.jws" target=_blank>http://localhost:8080/axis/Hello.jws</A> 浏览器显示如下结果： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
There is a Web Service here
<A href="http://localhost:8080/axis/Hello.jws?wsdl" target=_blank>Click to see the WSDL </A>
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>点击页面上的链接查看该Web服务对应的WSDL信息如下所示（我们将在下一小节简单介绍WSDL） </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
-&lt;wsdl:definitions
	targetNamespace="http://localhost:8080/axis/Hello.jws"
	xmlns="http://schemas.xmlsoap.org/wsdl/"
	xmlns="http://www.w3.org/2000/xmlns/"
	xmlns:apachesoap="http://xml.apache.org/xml-soap" 
	xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
	xmlns:impl="http://localhost:8080/axis/Hello.jws" 
	xmlns:intf="http://localhost:8080/axis/Hello.jws"
	xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"&gt;
	- &lt;wsdl:message name="helloRequest"&gt;
	&lt;wsdl:part name="name" type="xsd:string" /&gt; 
	&lt;/wsdl:message&gt;
	+ &lt;wsdl:message name="helloResponse"&gt;
	- &lt;wsdl:portType name="Hello"&gt;
	- &lt;wsdl:operation name="hello" parameterOrder="name"&gt;
	&lt;wsdl:input name="helloRequest" message="intf:helloRequest" /&gt; 
	&lt;wsdl:output name="helloResponse" message="intf:helloResponse" /&gt;
	&lt;/wsdl:operation&gt; 
	&lt;/wsdl:portType&gt;
	- &lt;wsdl:binding name="HelloSoapBinding" type="intf:Hello"&gt; 
	&lt;wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" /&gt;
	- &lt;wsdl:operation name="hello"&gt; 
	&lt;wsdlsoap:operation soapAction="" /&gt;
	- &lt;wsdl:input name="helloRequest"&gt;
	&lt;wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
		namespace="http://DefaultNamespace" /&gt;  
	&lt;/wsdl:input&gt;- &lt;wsdl:output name="helloResponse"&gt;  
	&lt;wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
		namespace="http://localhost:8080/axis/Hello.jws" /&gt;   
	&lt;/wsdl:output&gt; 
	&lt;/wsdl:operation&gt; 
	&lt;/wsdl:binding&gt;
	- &lt;wsdl:service name="HelloService"&gt;
	- &lt;wsdl:port name="Hello" binding="intf:HelloSoapBinding"&gt;
	&lt;wsdlsoap:address location="http://localhost:8080/axis/Hello.jws" /&gt; 
	&lt;/wsdl:port&gt;  
	&lt;/wsdl:service&gt; 
	&lt;/wsdl:definitions&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>到此我们已经完成了hello的Web服务了，那我们怎么告诉用户如何来使用该服务呢？我们只需要告诉用户我们的Web服务的URL地址：<A href="http://localhost:8080/axis/Hello.jws?wsdl" target=_blank>http://localhost:8080/axis/Hello.jws?wsdl</A> 就可以了！下一节我们将介绍如何通过这个地址来访问对应的Web服务。 </P>
<P><A name=4><SPAN class=atitle2>四． Web Service客户端开发</SPAN></A><BR>在这一节中我们将使用三种不同的语言来访问刚刚创建的Web服务，分别是JAVA、VB、VC。为了使用VB和VC访问Web服务，我们需要安装微软公司的Soap Toolkit 开发工具包，这个工具包可以从微软公司的主页 </P>
<P><A href="http://download.microsoft.com/download/xml/soap/2.0/W98NT42KMe/EN-US/SoapToolkit20.exe" target=_blank>http://download.microsoft.com/download/xml/soap/2.0/W98NT42KMe/EN-US/SoapToolkit20.exe</A> </P>
<P>下载，下载该软件包并使用默认方式安装即可。 </P>
<P>在开始客户端开发之前有两个概念我们必须先粗略的介绍一下。 </P>
<P>SOAP：简单对象访问协议。这是一种在松散的、分布的环境中使用XML对等地交换结构化的和类型化的信息提供了一个简单且轻量级的机制，它是一个基于XML的协议。它包括四个部分：SOAP封装（envelop），封装定义了一个描述消息中的内容是什么，是谁发送的，谁应当接受并处理它以及如何处理它们的框架；SOAP编码规则（encoding rules），用于表示应用程序需要使用的数据类型的实例; SOAP RPC表示(RPC representation)，表示远程过程调用和应答的协定;SOAP绑定（binding），使用底层协议交换信息。 </P>
<P>虽然这四个部分都作为SOAP的一部分，作为一个整体定义的，但他们在功能上是相交的、彼此独立的。特别的，信封和编码规则是被定义在不同的XML命名空间(namespace)中，这样使得定义更加简单。 </P>
<P>SOAP的主要设计目标是简明性和可扩展性。这就意味着有一些传统消息系统或分布式对象系统中的特性将不包含在SOAP的核心规范中。这些特性包括：分布式垃圾收集；批量消息传输/处理；对象引用；对象激活。 </P>
<P>WSDL：Web Service描述语言。使用了WSDL，我们就可以通过这种跨平台和跨语言的方法使Web Service代理的产生自动化。就像COM和CORBA的IDL文件，WSDL文件由客户和服务器约定。由于WSDL设计成可以绑定除SOAP以外的其他协议，这里我们主要关注WSDL在HTTP上和SOAP的关系。同样，由于SOAP目前主要用来调用远程的过程和函数，WSDL支持SOAP传输的文档规范。 </P>
<P>WSDL文档可以分为两部分。顶部分由抽象定义组成，而底部分则由具体描述组成。抽象部分以独立于平台和语言的方式定义SOAP消息，它们并不包含任何随机器或语言而变的元素。这就定义了一系列服务，截然不同的网站都可以实现。 </P>
<P><SPAN class=atitle3>1. JAVA客户端</SPAN><BR>使用AXIS的工具将使Web服务的访问和我们之前介绍的创建一个Web服务一样的简单。我们前面安装的AXIS环境中已经包含着这样的工具，它是一个JAVA类，类名为：org.apache.axis.wsdl.WSDL2Java。打开命令行窗口，转到AXIS目录下的WEB-INF子目录。确保Tomcat服务已经处于启动状态，键入命令 ： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Java -Djava.ext.dirs=lib org.apache.axis.wsdl.WSDL2Java http://localhost:8080/axis/Hello.jws?wsdl
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>该命令执行的结果是在当前所在目录下产生一个子目录 localhost/axis/Hello_jws，该目录下有四个JAVA源文件，它们分别是： </P>
<P>Hello.java 定义了Web服务接口，此例中只有一个hello方法。 </P>
<P>HelloService.java 定义了用于获取Web服务接口的方法。 </P>
<P>HelloServiceLocator.java 接口HelloService的具体实现。 </P>
<P>HelloSoapBindingStub.java Web服务客户端桩，通过该类与服务器交互。 </P>
<P>这四个JAVA类帮我们处理了大部分的逻辑，我们需要的仅仅是把这些类加到我们的项目然后创建一个我们自己的类来调用它们即可。为此我们新加一个类Main.java，为了方便，让这个类与刚产生的四个类都在同一个包下。内容如下： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//Main.java
package localhost.axis.Hello_jws;
public class Main{
public static void main(String[] args) throws Exception{
	HelloService service = new HelloServiceLocator();
	Hello hello = service.getHello();	
	System.out.println("Response:"+hello.hello("罐头"));	
	}
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>使用以下命令进行编译： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
javac -classpath lib\axis.jar;lib\jaxrpc.jar localhost\axis\Hello_jws\*.java
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>如果编译没有问题的话执行该测试程序：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java -Djava.ext.dirs=lib -cp . localhost.axis.Hello_jws.Main//运行结果：Response:你好罐头，欢迎来到Web服务的世界！
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>在WSDL2Java工具自动产生的几个类中，类HelloServiceLocator中保存这一些跟服务器相关的信息，例如URL地址等，当服务器的地址更改后但是服务并没有改动的时候直接修改该文件中的字符串定义，而无需重新生成这几个类。具体需要修改的内容，打开该文件便可一目了然。 </P>
<P><SPAN class=atitle3>2. VB客户端</SPAN><BR>有了微软SOAP toolkit，用VB调用Web服务也是一件令人愉快的事情。</P>
<P>打开VB开发环境新建一个标准EXE项目，打开工程(Project)菜单并选择引用打开组件引用对话框如下图所示：找到并选中Microsoft Soap Type Library。 </P>
<P align=center><IMG src="http://www-900.ibm.com/developerWorks/cn/webservices/ws-startaxis/image008.jpg"></P>
<P align=center><B>图 4</B></P>
<P>新建并编辑窗体如下图所示：</P>
<P align=center><IMG src="http://www-900.ibm.com/developerWorks/cn/webservices/ws-startaxis/4_vbclient.jpg"></P>
<P align=center><B>图 5</B></P>
<P>编辑按钮Call的点击事件处理程序如下：（注意窗体的控件名称要与程序中的名称对应）</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Private Sub callBtn_Click()    
'这种做法需要在工程中引用Soap Type Library  
'Dim soap As MSSOAPLib.SoapClient  
'Set soap = New MSSOAPLib.SoapClient
Dim soap  
Set soap = CreateObject("MSSOAP.SoapClient")   
On Error Resume Next
'soap.mssoapinit urlText.Text 
Call soap.mssoapinit(urlText.Text)   
If Err &lt;&gt; 0 Then   
	MsgBox "初始化SOAP失败： " + Err.Description       
	urlText.SetFocus 
Else       
	If Len(Trim(nameText.Text)) = 0 Then       
		MsgBox "请输入您的姓名！"          
		nameText.SetFocus    
	Else          
		responseText.Text = soap.hello(nameText.Text) 
	End If 
 End If
End Sub
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>保存项目并运行，输入姓名并点击按钮Call。 </P>
<P><SPAN class=atitle3>3. VC客户端</SPAN><BR>打开VC开发环境，新建项目HelloClient，项目类型为 Win32 Console Application的空项目。新建C++ Source File文件名为：HelloSoap.cpp，编辑文件内容如下： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
//#include "stdafx.h"
#include &lt;stdio.h&gt;
#import "msxml3.dll"
using namespace MSXML2;
//根据自己机器的情况修改下面语句中指定的路径
#import "C:\Program Files\Common Files\MSSoap\Binaries\mssoap1.dll" \	
	exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", \	
	"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib;

void Hello(){	
	ISoapSerializerPtr Serializer;
	ISoapReaderPtr Reader;	
	ISoapConnectorPtr Connector;
	// Connect to the service	
	Connector.CreateInstance(__uuidof(HttpConnector));
	Connector-&gt;Property["EndPointURL"] = "http://localhost:8080/axis/Hello.jws?wsdl";
	Connector-&gt;Connect();	
	// Begin message
	Connector-&gt;BeginMessage();	
	// Create the SoapSerializer	
	Serializer.CreateInstance(__uuidof(SoapSerializer));	
	// Connect the serializer to the input stream of the connector
	Serializer-&gt;Init(_variant_t((IUnknown*)Connector-&gt;InputStream));	

	// Build the SOAP Message	

	Serializer-&gt;startEnvelope("","","");	
	Serializer-&gt;startBody("");	
	Serializer-&gt;startElement("hello","","","");	
	Serializer-&gt;startElement("name","","","");
	Serializer-&gt;writeString("罐头");	
	Serializer-&gt;endElement();	
	Serializer-&gt;endElement();
	Serializer-&gt;endBody();	
	Serializer-&gt;endEnvelope();		
	
	// Send the message to the web service	
	Connector-&gt;EndMessage();  	
	
	// Let us read the response
	Reader.CreateInstance(__uuidof(SoapReader));	
	// Connect the reader to the output stream of the connector
	Reader-&gt;Load(_variant_t((IUnknown*)Connector-&gt;OutputStream), "");	

	// Display the result	
	printf("Response: %s\n", (const char*)Reader-&gt;RPCResult-&gt;text);  
}

int main()
{
	CoInitialize(NULL);
	Hello();	
	CoUninitialize();	
	
	return 0;	
}
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>编译并运行该项目。</P>
<P>本节只是为了演示如果通过VC来访问使用AXIS创建的Web服务，至于Soap toolkit的具体使用请参照soap toolkit的帮助手册，其他语言的访问请查阅相关的文档。 </P>
<P><A name=5><SPAN class=atitle2>五. AXIS集成</SPAN></A></P>
<P>为了让我们的WEB应用程序支持Web服务功能，我们需要将AXIS集成到我们的应用程序中。集成AXIS很简单，首先需要拷贝AXIS用到的几个JAR包文件，这些文件都在[AXIS]\WEB-INF\lib目录下，将这些文件拷贝到我们自己的应用目录下的WEB-INF\lib。另外如果你用的不是TOMCAT服务器那就需要拷贝activation.jar，这个JAR文件可以在[TOMCAT]\common\lib目录下找到！ </P>
<P>拷贝完JAR文件后就是web.xml的配置了，只需要把AXIS中的web.xml中的配置信息添加到我们自己应用程序中的web.xml中即可。最重要的是下面的内容： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;servlet&gt;  
	&lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;  
	&lt;display-name&gt;Apache-Axis Servlet&lt;/display-name&gt;   
	&lt;servlet-class&gt; 
		org.apache.axis.transport.http.AxisServlet  
	&lt;/servlet-class&gt;
&lt;/servlet&gt;

&lt;servlet-mapping&gt;   
	&lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;  
	&lt;url-pattern&gt;*.jws&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;

&lt;mime-mapping&gt;    
	&lt;extension&gt;wsdl&lt;/extension&gt;  
	&lt;mime-type&gt;text/xml&lt;/mime-type&gt;
&lt;/mime-mapping&gt;

&lt;mime-mapping&gt;   
	&lt;extension&gt;xsd&lt;/extension&gt;   
	&lt;mime-type&gt;text/xml&lt;/mime-type&gt;
&lt;/mime-mapping&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P><A name=6><SPAN class=atitle2>六 总结</SPAN></A><BR>到此文章告一段落，通过以上的演练，我相信你已经对Web服务有一个感性的认识，但是这个仅仅是开始，我们也只是很简单的介绍了Web服务的一些基本概念并演示了一个无法再简单的例子。Web服务还有很多其他高级的内容例如复杂类型、数据安全等没有涉及到，不过没有关系，万事开头难，希望本文能够促进大家理解和应用下一代的应用模式并给还没有动手试验的开发人员开一个好头。 </P><!-- RESOURCES--><img src ="http://www.blogjava.net/Victor/aggbug/1475.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-02-24 11:44 <a href="http://www.blogjava.net/Victor/articles/1475.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Axis部署Web服务时的常见问题及其解决方法(转)</title><link>http://www.blogjava.net/Victor/articles/1471.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Thu, 24 Feb 2005 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/Victor/articles/1471.html</guid><wfw:comment>http://www.blogjava.net/Victor/comments/1471.html</wfw:comment><comments>http://www.blogjava.net/Victor/articles/1471.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Victor/comments/commentRss/1471.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Victor/services/trackbacks/1471.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=168 align=right border=0>
<TBODY>
<TR><!-- Sidebar Gutter-->
<TD width=8><IMG height=21 alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width=5></TD>
<TD width=160><!-- Start TOC-->
<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-900.ibm.com/developerWorks/cn/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-900.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR>
<TR>
<TD align=right>
<TABLE cellSpacing=0 cellPadding=3 width="98%" border=0>
<TBODY>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/index.shtml#1">1 引言</A></TD></TR>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/index.shtml#2">2 使用Axis时的常见问题及其解决方法</A></TD></TR>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/index.shtml#3">3 小结</A></TD></TR>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/index.shtml#resources">参考资料</A></TD></TR>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/index.shtml#author1">关于作者</A></TD></TR>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/index.shtml#rating">对于本文的评价</A></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-900.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR>
<TR>
<TD align=middle background=/developerWorks/cn/i/bg-gold.gif height=5><A class=nav href="http://www-900.ibm.com/developerWorks/cn/webservices/index.shtml"><B>在 Web 服务专区还有：</B></A></TD></TR>
<TR>
<TD width=160 bgColor=#666666 height=1><IMG height=1 alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR>
<TR>
<TD align=right>
<TABLE cellSpacing=0 cellPadding=3 width="98%" border=0>
<TBODY>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/xml-onlinecourse-bytitle?OpenView&amp;Count=500iew&amp;Count=500">教学</A></TD></TR>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/cntools.nsf/dw/xml-tools-byzone?OpenDocument&amp;count=10">工具与产品</A></TD></TR>
<TR>
<TD><A href="http://www-900.ibm.com/developerWorks/cn/cnpapers.nsf/xml-papers-bynewest?OpenView&amp;count=500">所有的文章</A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!-- End Related dW Content Area-->
<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-900.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR>
<TR>
<TD width=150 bgColor=#ffffff colSpan=2 height=2><IMG height=2 alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width=160></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!-- END STANDARD SIDEBAR AREA--></TD></TR></TBODY></TABLE><!-- START SUBTITLE AND CONTENT-->
<P><A href="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/index.shtml#author1">刘红涛</A> (<A href="mailto:redwaveht@hotmail.com">redwaveht@hotmail.com</A>)<BR><BR>2003 年 11 月</P>
<BLOCKQUOTE>本文详细介绍了在Linux环境下以Apache Axis+ Resin作为Web服务平台部署Web服务时的常见问题及解决方法。衷心希望本文对Web服务的开发人员或对Web服务感兴趣的读者能起到一定的帮助作用。</BLOCKQUOTE>
<P><A name=1><SPAN class=atitle2>1 引言</SPAN></A><BR>随着Web服务技术的发展和成熟，其方便性和易用性已逐渐被人们所接受，越来越多的合作伙伴之间开始利用Web服务来实现合作方之间的数据接口。使用Apache Axis和Linux平台是一种低成本的Web服务解决方案，但Apache Axis文档的FAQ对开发者来说内容还不够丰富，本文作者将自己使用Axis时遇到的问题和解决方法整理成文，奉献给Web服务的开发人员和对此感兴趣的读者朋友，旨在帮助大家节约一些宝贵的时间。有关Web服务的基础知识，读者可以阅读参考文献中推荐的文档。作者未在文中介绍Apache和Resin的安装方法，读者可以参考相关网站的说明文档。 </P>
<P>作者使用的软件环境如下。 </P>
<P>操作系统：Red Hat Linux 7.2 </P>
<P>Web服务器: Apache 1.3.27 </P>
<P>应用服务器：Resin 2.1.8 ( <A href="http://www.caucho.com/">http://www.caucho.com/</A> ) </P>
<P>SOAP服务器：Apache Axis 1.1 </P>
<P>XML解析器：Xerces 2.5.0，Xalan 2.5.1 </P>
<P>JDK版本：JDK 1.4.1 </P>
<P><A name=2><SPAN class=atitle2>2 使用Axis时的常见问题及其解决方法</SPAN></A></P>
<P>2.1 Axis运行需要哪些jar文件 </P>
<P>对Axis解包后，将axis-1_1/webapps/axis/WEB-INF/lib/目录下的jar文件复制到/usr/local/apache/htdocs/WEB-INF/lib目录下(Web应用程序的目录)。应包括以下jar文件。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
axis-ant.jar
axis.jar
commons-discovery.jar
commons-logging.jar
jaxrpc.jar
log4j-1.2.8.jar
name.txt
saaj.jar
wsdl4j.jar
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>如果需要使用axis提供的测试页面，还要将axis-1_1/webapps/axis/目录下的文件复制到/usr/local/apache/htdocs/axis/目录下。应包括以下文件。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
EchoHeaders.jws
fingerprint.jsp
happyaxis.jsp
index.html
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P><SPAN class=atitle3>2.2 应该使用哪一种XML解析器</SPAN></P>
<P>XML解析器选用不当，经常会导致使用Apache Axis时出现一些莫名其妙的问题。 </P>
<P>由于Apache Axis 并未对Resin内置的xml解析器进行过测试，因此推荐读者使用已通过测试的Xerces xml解析器。可以从 <A href="http://xml.apache.org/xalan-j/index.html">http://xml.apache.org/xalan-j/index.html</A> 处下载Xalan的Java版XSLT处理器，其中包含了Xerces的Java版XML解析器，不需要再单独下载xml解析器。 </P>
<P>Xalan 2.5.1解包后，将bin/目录下的xercesImpl.jar、xml-apis.jar和xalan.jar复制到resin安装目录的lib/目录下，例如/usr/local/resin/lib。 </P>
<P>编辑/etc/目录下的profile文件，找到设置CLASSPATH环境变量的位置，在其后加入下面的内容(B shell)。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
XMLPARSER=$RESIN_HOME/lib/xalan.jar:$RESIN_HOME/lib/xercesImpl.jar:$RESIN_HOME/lib/xml-apis.jar
export CLASSPATH=$XMLPARSER:$CLASSPATH
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>2.2.1 注意事项<BR>如果CLASSPATH中包含其它的XML解析器设置，应将其从CLASSPATH环境变量的设置中去掉，以免发生冲突。 </P>
<P><SPAN class=atitle3>2.3 如何在Resin中使用Xerces的XML解析器</SPAN></P>
<P>通过修改resin.conf将resin的XmlParser置换为Xerces的XmlParser。在resin.conf对应的Web应用程序配置中加入以下设置。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;system-property javax.xml.transform.TransformerFactory=<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"org.apache.xalan.processor.TransformerFactoryImpl"/&gt;
&lt;system-property javax.xml.parsers.DocumentBuilderFactory=<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"/&gt;
&lt;system-property javax.xml.parsers.SAXParserFactory=<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"org.apache.xerces.jaxp.SAXParserFactoryImpl"/&gt;
&lt;system-property org.xml.sax.driver="org.apache.xerces.parsers.SAXParser"/&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>配置完成后重新启动Resin。</P>
<P><SPAN class=atitle3>2.4 如何排除XML解析器出现的异常</SPAN></P>
<P>2.4.1 问题描述</P>
<P>使用http://test.com/axis查看已部署的服务时出现Axis内部错，显示有关WSDD配置的异常信息。如果把WEB-INF目录下的server-config.wsdd删除，再查看就正常了，但只能看到AdminService和Version两个系统缺省的服务，后来部署的服务都看不到了。 </P>
<P>2.4.2 原因分析</P>
<P>Axis会在WEB应用程序的WEB-INF/目录下自动生成一个名字为server-config.wsdd的xml文件，其中记录了已部署的WEB服务。每部署一个新的WEB服务时，Axis都会将新服务的描述信息加入到server-config.wsdd中。 </P>
<P>故障站点使用的XmlParser是resin内置的XmlParser，Axis并未对其对进行过兼容性测试，查看WEB服务信息时需要从server-config.wsdd(这是一个xml文件)取得已部署的WEB服务描述信息，当server-config.wsdd的内容较复杂时，resin内置的XmlParser因某种原因出现异常，导致Axis内部错误。Server-config.wsdd中记录的Web服务描述信息较少时不会出现异常。 </P>
<P>2.4.3 解决方法 </P>
<P>修改resin.conf，将resin的XmlParser置换为Xerces的XmlParser。置换方法参见2.3节。</P>
<P>2.4.4 小结 </P>
<P>如果Axis报告的错误中有关于xml解析器的错误，建议读者参照本小节描述的方法更换应用服务器的xml解析器，将会有助于问题的解决。 </P>
<P><SPAN class=atitle3>2.5 如何将Axis集成到Resin或其它应用服务器</SPAN></P>
<P>Axis是以Servlet的方式运行的，而Resin的作用相当于Servlets容器(Container)，因此只要配置得当，就可以使Axis在Resin环境中运行，这一点也适用于Resin以外的其它应用服务器。在Resin中配置Axis的方法如下。 </P>
<P>将axis-1_1/webapps/axis/WEB-INF/web.xml中的Servlet配置项复制到resin.conf中对应的Web应用程序配置中。通常应包括以下内容。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
	&lt;!-- Axis Web-Service Configuration Start --&gt;
		&lt;servlet&gt;
		  &lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;
		  &lt;display-name&gt;Apache-Axis Servlet&lt;/display-name&gt;
		  &lt;servlet-class&gt;
		      org.apache.axis.transport.http.AxisServlet
		  &lt;/servlet-class&gt;
		&lt;/servlet&gt;

		&lt;servlet&gt;
		  &lt;servlet-name&gt;AdminServlet&lt;/servlet-name&gt;
		  &lt;display-name&gt;Axis Admin Servlet&lt;/display-name&gt;
		  &lt;servlet-class&gt;
		      org.apache.axis.transport.http.AdminServlet
		  &lt;/servlet-class&gt;
		  &lt;load-on-startup&gt;100&lt;/load-on-startup&gt;
		&lt;/servlet&gt;

		&lt;servlet&gt;
		  &lt;servlet-name&gt;SOAPMonitorService&lt;/servlet-name&gt;
		  &lt;display-name&gt;SOAPMonitorService&lt;/display-name&gt;
		  &lt;servlet-class&gt;
		      org.apache.axis.monitor.SOAPMonitorService
		  &lt;/servlet-class&gt;
		  &lt;init-param&gt;
		    &lt;param-name&gt;SOAPMonitorPort&lt;/param-name&gt;
		    &lt;param-value&gt;5001&lt;/param-value&gt;
		  &lt;/init-param&gt;
		  &lt;load-on-startup&gt;100&lt;/load-on-startup&gt;
		&lt;/servlet&gt;

		&lt;servlet-mapping&gt;
		  &lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;
		  &lt;url-pattern&gt;/axis/servlet/AxisServlet&lt;/url-pattern&gt;
		&lt;/servlet-mapping&gt;

		&lt;servlet-mapping&gt;
		  &lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;
		  &lt;url-pattern&gt;*.jws&lt;/url-pattern&gt;
		&lt;/servlet-mapping&gt;

		&lt;servlet-mapping&gt;
		  &lt;servlet-name&gt;AxisServlet&lt;/servlet-name&gt;
		  &lt;url-pattern&gt;/services/*&lt;/url-pattern&gt;
		&lt;/servlet-mapping&gt;

		&lt;servlet-mapping&gt;
		  &lt;servlet-name&gt;SOAPMonitorService&lt;/servlet-name&gt;
		  &lt;url-pattern&gt;/SOAPMonitor&lt;/url-pattern&gt;
		&lt;/servlet-mapping&gt;

		&lt;!-- uncomment this if you want the admin servlet --&gt;
	&lt;!--	
		 &lt;servlet-mapping&gt;
		   &lt;servlet-name&gt;AdminServlet&lt;/servlet-name&gt;
		   &lt;url-pattern&gt;/axis/servlet/AdminServlet&lt;/url-pattern&gt;
		 &lt;/servlet-mapping&gt;
	--&gt;	
		&lt;!-- currently the W3C havent settled on a media type for WSDL;
		  http://www.w3.org/TR/2003/WD-wsdl12-20030303/#ietf-draft
		  for now we go with the basic 'it's XML' response --&gt;
		&lt;mime-mapping&gt;
		  &lt;extension&gt;wsdl&lt;/extension&gt;
		   &lt;mime-type&gt;text/xml&lt;/mime-type&gt;
		&lt;/mime-mapping&gt;

		&lt;mime-mapping&gt;
		  &lt;extension&gt;xsd&lt;/extension&gt;
		  &lt;mime-type&gt;text/xml&lt;/mime-type&gt;
		&lt;/mime-mapping&gt;

	  &lt;!-- Axis Web-Service Configuration End --&gt;	
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P><SPAN class=atitle3>2.6 Axis提供了哪些开发工具</SPAN></P>
<P>Apache Axis提供了WSDL2Java和Java2WSDL两个开发工具。 </P>
<P>WSDL2Java利用已知的WSDL文件生成服务端和客户端代码。该WSDL文件可以是由合作伙伴提供的，也可以是利用Java2WSDL生成的。Java2WSDL根据已有的Java类文件生成WSDL文件，Java类文件可以是接口类文件，并不需要实现细节。 </P>
<P>此外Axis还提供了SoapMonitorApplet和TCPMon工具，可用于监测Web服务。 </P>
<P><SPAN class=atitle3>2.7 如何生成Web服务的服务端和客户端代码</SPAN></P>
<P>2.7.1 生成或取得WSDL文件</P>
<P>Java2WSDL是Axis提供的利用Java类文件得到WSDL文件的工具。类文件可以使用接口文件编译生成，例如下面的接口文件SoftwarePrice.java。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package samples.userguide.example6;
/**
 * Interface describing a web service to set and get software prices.
 **/
public interface SoftwarePrice {
    public void setWidgetPrice(String softWareName, String price);
    public String getWidgetPrice(String softWareName);
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>编译SoftwarePrice.java。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
javac SoftwarePrice.java
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>将SoftwarePrice.class复制到正确的package路径下。 </P>
<P>执行下面的命令: </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.wsdl.Java2WSDL -o sp.wsdl <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-l"http://test.com:80/services/SoftwarePrice" <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-n "urn:SoftwarePrice" -p"samples.userguide.example6" "urn:Example6" <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;samples.userguide.example6.SoftwarePrice
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>各参数的含义如下。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
-o：指定输出的WSDL文件的文件名。<BR>
-l：指定服务的位置。<BR>
-n：WSDL文件的目标名字空间。<BR>
-p：指定从package到名字空间的映射，这里可以有多个映射。<BR>
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>最后面的类文件包含了Web服务的接口。 </P>
<P>该命令执行后，将生成sp.wsdl文件。 </P>
<P>如果按CLASSPATH的设置找不到指定的类文件，Axis将报告异常，如下所示。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java.lang.ClassNotFoundException: samples.userguide.example6.SoftwarePrice
        at java.net.URLClassLoader$1.run(URLClassLoader.java:198)
        at java.security.AccessController.doPrivileged(Native Method)
		……
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>如果出现上面的问题，请检查是否已将有关类文件复制到正确的位置或CLASSPATH设置是否正确。 </P>
<P>生成WSDL文件以后，就可以利用Axis提供的WSDL2Java工具生成Web服务的服务端代码和客户端代码了。 </P>
<P>2.7.1.1 注意事项 </P>
<P>WSDL文件也可以由合作伙伴提供。这种情况下合作伙伴往往是Web服务的提供者或标准接口的制定者，开发者只要按照既定的WSDL文件生成客户端或服务端代码就可以了。 </P>
<P>2.7.2 生成客户端或服务端代码 </P>
<P>WSDL2Java工具用于从WSDL文件生成客户端存根（stub）代码，服务端框架（skeleton）代码以及WSDL中的数据类型文件(生成与之对应的Java代码)。开发人员只需向框架代码中补充相关的业务逻辑代码即可得到完整的Web服务代码，因此该工具极大地减轻了开发人员的编码负担。WSDL2Java的使用举例如下。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.wsdl.WSDL2Java --server-side --skeletonDeploy true MyService.wsdl
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>执行上述命令后将生成下列文件。 </P>
<P>
<TABLE cellSpacing=0 borderColorDark=#000000 cellPadding=5 width="90%" align=center borderColorLight=#ffffff border=1>
<TBODY>
<TR>
<TD width="20%">No.</TD>
<TD width="40%">文件</TD>
<TD width="40%">用途</TD></TR>
<TR>
<TD>1.</TD>
<TD>deploy.wsdd</TD>
<TD>MyService服务的部署描述文件</TD></TR>
<TR>
<TD>2.</TD>
<TD>MyService.java</TD>
<TD>MyService服务的接口文件</TD></TR>
<TR>
<TD>3.</TD>
<TD>MyServiceService.java</TD>
<TD>获得MyService服务的接口文件</TD></TR>
<TR>
<TD>4.</TD>
<TD>MyServiceServiceLocator.java</TD>
<TD>实现MyServiceService接口</TD></TR>
<TR>
<TD>5.</TD>
<TD>MyServiceSoapBindingImpl.java</TD>
<TD>实现MyService接口，应向其中补充业务逻辑</TD></TR>
<TR>
<TD>6.</TD>
<TD>MyServiceSoapBindingSkeleton.java</TD>
<TD>MyService服务的服务端框架代码,实现MyService, org.apache.axis.wsdl.Skeleton接口</TD></TR>
<TR>
<TD>7.</TD>
<TD>MyServiceSoapBindingStub.java</TD>
<TD>MyService服务的客户端存根代码,实现MyService接口</TD></TR>
<TR>
<TD>8.</TD>
<TD>undeploy.wsdd</TD>
<TD>注销MyService服务的部署描述文件</TD></TR></TBODY></TABLE></P>
<P>2.7.3 编写Web服务客户端代码</P>
<P>Web服务的客户端程序完成对Web服务的调用，其程序结构如下。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import com.chinavnet.zx.service.v1_0.*;// WSDL2Java生成的package的名字空间;
public class TestClient {
    public static void main (String[] args) throws Exception
&nbsp;&nbsp;&nbsp; {
	com.chinavnet.zx.service.v1_0.SPInterfaceForVNetLocator locator = <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;new  SPInterfaceForVNetLocator();//获得一个locator对象
	locator.setMaintainSession(true);
	com.chinavnet.zx.service.v1_0.SPInterfaceForVNetSoap service = <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;locator.getSPInterfaceForVNetSoap();//获得服务对象
	com.chinavnet.zx.service.v1_0.DetailLedgerFeedbackResult feedbackRes = null;
	//初始化Web服务中定义的数据类型
         try {
	&nbsp;&nbsp;&nbsp;feedbackRes =  service.generalLedgerFeedback();//调用Web服务的方法并取得返回值
	&nbsp;&nbsp;&nbsp;System.out.println("FeedbackResult :");
	&nbsp;&nbsp;&nbsp;if(feedbackRes != null)
	&nbsp;&nbsp;&nbsp;{
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("SPID: " +feedbackRes.getSPID());
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("errorDesc: " +feedbackRes.getErrorDescription());
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("result: " +feedbackRes.getResult());
	&nbsp;&nbsp;&nbsp;}else
	&nbsp;&nbsp;&nbsp;{
	&nbsp;&nbsp;&nbsp;System.out.println("feedbackRes is null!");
	}
        } catch (java.rmi.RemoteException re) {
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //throw new junit.framework.AssertionFailedError("Remote Exception caught: "+re );
 	re.printStackTrace();
     }
}//End of main()
}//End of TestClient class
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>测试客户端程序是非常简单的，将客户端程序编译后，执行"java TestClient"即可。</P>
<P><SPAN class=atitle3>2.8 如何编写服务端代码</SPAN></P>
<P>向MyServiceSoapBindingImpl.java添加相关的业务逻辑代码后，将WSDL2Java生成的源程序编译，打包成jar文件，复制到/usr/local/apache/htdocs/WEB-INF/lib/目录下。 </P>
<P><SPAN class=atitle3>2.9 如何发布Web服务</SPAN></P>
<P>有了deploy.wsdd文件并准备好类文件后，就可以发布MyService服务了。Axis在安装后自动发布了AdminService，利用它可以发布新的Web服务。方法如下。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.client.AdminClient <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;-lhttp://localhost:80/services/AdminService deploy.wsdd
上面的命令执行后，有如下提示。
Processing file deploy.wsdd
&lt;Admin&gt;Done processing&lt;/Admin&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P><SPAN class=atitle3>2.10 执行WSDL2Java时报告" 类型被引用但未定义"</SPAN></P>
<P>2.10.1 问题描述</P>
<P>执行WSDL2Java时报告下面的异常：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java.io.IOException: Type {http://schemas.xmlsoap.org/wsdl/} <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;ArrayOfFailedRecord is referenced but not defined.
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>2.10.2 原因分析</P>
<P>出现上述情况可能的原因有：</P>
<P>类型未定义就被引用。</P>
<P>使用了错误的名字空间。 </P>
<P>WSDL文件中存在输入错误。 </P>
<P>2.10.3 解决方法</P>
<P>经过仔细检查发现wsdl文件中的</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;s:element minOccurs="0" maxOccurs="1" name="FailedRecords" <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type="s0:ArrayOfFailedRecord" /&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>的type=" s0:ArrayOfFailedRecord"中的s0:前面有一个空格，将空格删除后问题解决了。</P>
<P>2.10.4 小结</P>
<P>WSDL文件中出现的编辑错误有可能导致执行WSDL2Java时出现"类型被引用但未定义"错误。</P>
<P><SPAN class=atitle3>2.11 server-config.wsdd的作用是什么</SPAN></P>
<P>server-config.wsdd记录了axis已发布的Web服务的描述信息。</P>
<P>2.12 发布Web服务时报告"Exception:: (404)Not Found"</P>
<P>2.12.1 问题描述</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
执行java org.apache.axis.client.AdminClient <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-lhttp://localhost:80/services/AdminService deploy.wsdd命令发布WEB服务时报告如下内容。
Processing file deploy.wsdd
Exception:: (404)Not Found
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>Apache的access_log显示如下。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
127.0.0.1 - - [17/Sep/2003:15:56:33 +0800] "POST /services/AdminService HTTP/1.0" 404 293
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>2.12.2 原因分析</P>
<P>出现这个问题的比较简单的原因可能是端口不对，或URL路径不对(例如路径中多写了目录)，Axis的SoapMonitor服务按指定的端口或URL找不到AdminService，需要检查正确的端口号和URL路径。 </P>
<P>比较深层次的原因就复杂得多了。</P>
<P>本文作者遇到的是下面的情况。 </P>
<P>AdminService是一个已发布的WEB服务。 </P>
<P>AdminClient使用soap协议同AdminService通信，需要按指定的端口和URL定位AdminService，而原来使用的resin.conf中，有两个web-app配置项，配置A的摘要如下所示。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;host id=' '&gt;
      &lt;app-dir&gt;/usr/local/apache/htdocs/&lt;/app-dir&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>配置B的摘要如下所示。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;host id='test.com'&gt;
      &lt;app-dir&gt;/usr/local/apache/htdocs/esales/&lt;/app-dir&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>Axis的有关配置在配置B中。</P>
<P>Httpd.conf中的DocumentRoot是/usr/local/apache/htdocs/。</P>
<P>使用java org.apache.axis.client.AdminClient -lhttp://localhost:80/services/AdminService deploy.wsdd发布WEB服务时，需要按http协议解析url: //localhost:80/services/AdminService。由于htdocs/目录下并没有services/目录(services是Axis在resin.conf中的servlet-mapping)，因此应用服务器按resin.conf中的配置去定位AdminService ，因为url的主机名为localhost，因此resin不会到&lt;host id='test.com'&gt;(配置B)中去定位url，而是到配置A中去定位，在配置A中并没有Axis的配置，找不到与AdminService相关的url，因此报告了前面描述的异常情况。 </P>
<P>2.12.3 解决方法</P>
<P>知道了原因后，按如下方法就可以解决了。</P>
<P>将配置B中全部关于Axis的配置移到配置A中，注意相应地要改变/etc/profile中的CLASSPATH变量的值。 </P>
<P>保存resin.conf后，重新登录服务器(使新的CLASSPATH生效)，重新启动resin。再次发布WEB服务。 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.client.AdminClient <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;-lhttp://localhost:80/services/AdminService deploy.wsdd
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>屏幕显示如下。 
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Processing file deploy.wsdd
&lt;Admin&gt;Done processing&lt;/Admin&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>WEB服务终于发布成功了。 </P>
<P>在发布成功的情况下apache的access_log中会看到如下的log： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
tail -f access_log|grep vice  
127.0.0.1 - - [17/Sep/2003:16:05:00 +0800] <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;"POST /services/AdminService HTTP/1.0" 200 296 "-" "Axis/1.1"
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>2.12.4 小结</P>
<P>本小节描述的问题可能是读者遇到的出现最多的异常，但原因并不是唯一的。遇到该问题时，建议读者冷静分析出现问题的可能原因，多实验几种解决方法，最终一定可以成功！ </P>
<P><SPAN class=atitle3>2.13 如何监测SOAP请求和响应流</SPAN> </P>
<P>2.13.1 引言 </P>
<P>发布了Web服务以后，如何观察请求和响应数据呢？记录运行日志是一种传统且有效的方法，但对于调试程序来讲还不够方便和直观。值得欣慰的是，Axis为我们提供了在客户端观察SOAP请求和响应流数据的工具SoapMonitor，经过适当配置后，可以实时地观察到Web服务的SOAP请求和响应数据。SoapMonitor是一个Java Applet程序，通过浏览器下载到客户端运行。下面就介绍SoapMonitor的配置和使用方法。 </P>
<P>2.13.2 准备SOAPMonitor 小应用程序</P>
<P>在Axis 1.1中，没有为我们编译SOAPMonitor.java程序，但我们可以在axis-1_1/webapps/axis/目录下找到名字为SOAPMonitorApplet.java的源程序文件，自己进行编译。该程序编译后，会生成如下的类文件： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
SOAPMonitorApplet.class
SOAPMonitorApplet$ServiceFilterPanel.class
SOAPMonitorApplet$SOAPMonitorData.class
SOAPMonitorApplet$SOAPMonitorFilter.class
SOAPMonitorApplet$SOAPMonitorPage.class
SOAPMonitorApplet$SOAPMonitorTableModel.class
SOAPMonitorApplet$SOAPMonitorTextArea.class
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>读者需要把上述类文件复制到自己的Web应用程序目录下，本例中是/usr/local/apache/htdocs/目录。 </P>
<P>在浏览器地址样中输入SOAPMonitor的地址，例如 <A href="http://test.com/SOAPMonitor">http://test.com/SOAPMonitor</A> ，浏览器会提示用户正在下载Applet程序，下载完毕后，读者可以在浏览器窗口中看到如图4-1所示的用户界面。如果上述类文件的位置不正确，浏览器会报告"找不到类"的错误。此时应检查是否已将上述类文件复制到正确的目录下。 </P>
<P align=center><IMG src="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/image01.gif" border=0></P>
<P align=center><B>图 2-1 SOAPMonitor的界面</B></P>
<P>2.13.3 发布SOAPMonitor服务</P>
<P>图2-1所示的SOAPMonitor界面出现后，并不意味着就可以观察到Web服务的SOAP请求流与响应流了，首先需要发布SOAPMonitorService。该Web服务是由Axis提供的，但需要由用户自己进行发布，方法如下。 </P>
<P>获得SOAPMonitor服务的WSDD文件deploy.wsdd，内容如下(也可到 <A href="http://www.sosnoski.com/presents/java-xml/axis/axis-monitor.html">http://www.sosnoski.com/presents/java-xml/axis/axis-monitor.html</A> 复制该文件)： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;deployment xmlns="http://xml.apache.org/axis/wsdd/" 
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&gt; 
  
  &lt;handler name="soapmonitor" 
      type="java:org.apache.axis.handlers.SOAPMonitorHandler"&gt; 
    &lt;parameter name="wsdlURL" 
      value="/axis/SOAPMonitorService-impl.wsdl"/&gt; 
    &lt;parameter name="namespace" 
      value="http://tempuri.org/wsdl/2001/12/SOAPMonitorService-impl.wsdl"/&gt; 
    &lt;parameter name="serviceName" value="SOAPMonitorService"/&gt; 
    &lt;parameter name="portName" value="Demo"/&gt; 
  &lt;/handler&gt; 
  
  &lt;service name="SOAPMonitorService" provider="java:RPC"&gt; 
    &lt;parameter name="allowedMethods" value="publishMessage"/&gt; 
    &lt;parameter name="className" 
      value="org.apache.axis.monitor.SOAPMonitorService"/&gt; 
    &lt;parameter name="scope" value="Application"/&gt; 
  &lt;/service&gt; 
&lt;/deployment&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>发布SOAPMonitor服务：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.client.AdminClient <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;-lhttp://localhost:80/services/AdminService deploy.wsdd
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>2.13.4 修改deploy.wsdd</P>
<P>发布SOAPMonitor服务后，还要对被监测的Web服务进行配置。方法是先注销该Web服务，然后修改该服务对应的WSDD文件，在其中增加请求流和响应流的配置，否则是观测不到SOAP请求和响应流的。 </P>
<P>注销Web服务的方法如下。 </P>
<P>进入该Web服务的undeploy.wsdd文件所在的目录，执行 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
Java org.apache.axis.client.AdminClient <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;-lhttp://localhost:80/services/AdminService undeploy.wsdd
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>修改WSDD文件的方法如下。 </P>
<P>以Axis提供的MyService为例，对MyService的WSDD文件做如下修改。 </P>
<P>修改前: </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
... 
 &lt;service name="MyService" provider="java:RPC"&gt; 
  &lt;parameter name="className" value="samples.userguide.example3.MyService"/&gt; 
  &lt;parameter name="allowedMethods" value="*"/&gt; 
  ... 
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>修改后: (有下划线的行是新加入的内容)</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
... 
 &lt;service name="MyService" provider="java:RPC"&gt; 
    &lt;requestFlow&gt; 
        &lt;handler type="soapmonitor"/&gt; 
    &lt;/requestFlow&gt; 
    &lt;responseFlow&gt; 
        &lt;handler type="soapmonitor"/&gt; 
    &lt;/responseFlow&gt; 
  &lt;parameter name="className" value="samples.userguide.example3.MyService"/&gt; 
  &lt;parameter name="allowedMethods" value="*"/&gt; 
  ... 
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>修改WSDD文件后，重新发布MyService服务：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.client.AdminClient <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;-lhttp://localhost:80/services/AdminService deploy.wsdd
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>2.13.5 启动SOAPMonitor小应用程序</P>
<P>按2.13.2节介绍的方法启动SOAPMonitor小应用程序。</P>
<P>使用客户端程序测试MyService服务： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
#  java samples.userguide.example3.Client <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;-lhttp://vnet.sohu.com/services/MyService "test"
结果为：You typed : test
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>注意，一定要先启动SOAPMonitor小应用程序，后调用Web服务才能观察到SOAP请求和响应流。</P>
<P>2.13.6 观察SOAP请求和响应流</P>
<P></P>
<P>这时在SoapMonitorApplet的窗口中可以观察到SOAP和请求和响应消息，如图2-2所示。 </P>
<P align=center><IMG src="http://www-900.ibm.com/developerWorks/cn/webservices/ws-axisfaq/image02.gif" border=0></P>
<P align=center><B>图2-2 MyService的Soap请求和响应流</B></P>
<P><SPAN class=atitle3>2.14 使用客户端程序测试Web服务时报告"Exception:: (404)Not Found"</SPAN></P>
<P>2.14.1 问题描述</P>
<P>WEB服务接口编写完成并发布后，客户端测试程序收不到WEB服务的返回结果，Apache或Resin的log中也看不到访问记录。但测试程序返回结果为0(成功)，在没有部署该WEB服务的情况下，也是这个结果，因此怀疑调用的是WSDL文件的提供者自己测试用的WEB服务接口，可能与另一方提供的WSDL文件有关。 </P>
<P>2.14.2 原因分析</P>
<P>合作伙伴调用WEB服务就能够成功，从WEB服务主机自己的客户端调用就接收不到数据，估计与合作伙伴提供的WSDL文件有关，该WSDL文件影响了WSDL2Java生成的客户端stub代码。检查stub代码，发现其soapAction都指向了合作伙伴的测试地址。 </P>
<P>2.14.3 解决方法</P>
<P>修改stub代码中的soapAction，改为sp(Service Provider)自己的WEB服务URL。重新编译程序并发布Web服务，问题解决。 </P>
<P>_call.setSOAPActionURI("sp自己的Web服务地址"); </P>
<P>2.14.4 小结</P>
<P>本小节描述的问题出现于Web服务提供方按合作伙伴统一提供的WSDL文件生成客户端代码的情况。遇到这类问题，读者可直接检查WSDL2Java自动生成的代码的有关部分。 </P>
<P>在WSDL文件中可以看到与下列内容相似的设置。</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
&lt;wsdl:service name="SPInterfaceForVNet"&gt;
 &lt;wsdl:port binding="impl:SPInterfaceSoapSoapBinding" name="SPInterfaceForVNetSoap"&gt;
  &lt;wsdlsoap:address location="http://test.com/services/SPInterfaceSoap" /&gt; 
  &lt;/wsdl:port&gt;
  &lt;/wsdl:service&gt;
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>其中<?XML:NAMESPACE PREFIX = WSDLSOAP /><WSDLSOAP:ADDRESS location="http://test.com/services/SPInterfaceSoap"></WSDLSOAP:ADDRESS>即为已发布的WEB服务的URL，供客户端调用。 </P>
<P><SPAN class=atitle3>2.15 如何列出已发布的Web服务</SPAN></P>
<P>使用下面的命令： </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.client.AdminClient -lhttp://localhost:80/services/  list
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>实际上该命令的输出就是server-config.wsdd文件的内容。 </P>
<P><SPAN class=atitle3>2.16 发布Web服务时报告"调用目标异常"</SPAN> </P>
<P>2.16.1 问题描述 </P>
<P>在部署WEB服务时报告：java.lang.reflect.InvocationTargetException异常。 </P>
<P>2.16.2 原因分析 </P>
<P>在WSDL文件中自定义的名字空间与Axis的services名字空间冲突。 </P>
<P>2.16.3 解决方法 </P>
<P>修改WSDL文件，将services改为别的名字。 </P>
<P><SPAN class=atitle3>2.17 WSDL文件中的targetNamespace的作用是什么</SPAN> </P>
<P>TargetNamespace指明目标名字空间，用于验证xml文档。 </P>
<P>在WSDL文件中，&lt;definitions&gt;中的targetNamespace与&lt;types&gt;中的targetNamespace应保持一致。 </P>
<P><SPAN class=atitle3>2.18 Web服务部署成功但检测不到</SPAN> </P>
<P>2.19 客户端程序找不到可用的Web服务</P>
<P>2.19.1 问题描述</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
执行 
java org.apache.axis.client.AdminClient <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;-lhttp://localhost:80/services/AdminService deploy.wsdd
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>时显示&lt;Admin&gt;Done processing&lt;/Admin&gt;，却检查不到新的服务，在WEB-INF/目录下的server-config.wsdd文件中也看不到相应的配置项。 </P>
<P>2.19.2 原因分析</P>
<P>新的WEB服务的类文件没有重新编译并复制到正确的位置。 </P>
<P>2.19.3 解决方法</P>
<P>WEB服务的类文件可以打包为jar文件。在CLASSPATH环境变量中正确设置jar文件的路径。例如：</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
jar cvf  SPInterfaceSoap.jar com/zx/service/v1_0/*.class
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>将SPInterfaceSoap.jar复制到WEB-INF/lib/目录下，重新发布WEB服务。 </P>
<P><SPAN class=atitle3>2.20 如何排除原因不明的错误</SPAN></P>
<P>有时，在对WEB服务执行客户端测试时会出现慕名其妙的错误。为保证环境是正确的，可以按下面的步骤排除原因不明的错误。 </P>
<P>1. Undeploy被测试的服务 </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
java org.apache.axis.client.AdminClient <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;-lhttp://localhost:80/services/AdminService undeploy.wsdd
</CODE></PRE></TD></TR></TBODY></TABLE></P>
<P>2. 重新启动resin: /usr/local/resin/bin/httpd.sh restart(如果是其它的应用服务器，请按相应的方法重新启动) </P>
<P>3. 重新发布WEB服务 </P>
<P>4. 进行测试 </P>
<P><A name=3><SPAN class=atitle2>3 小结</SPAN></A><BR>目前有很多可以使用的应用服务器和Web服务平台软件，本文仅以Apache Axis和Resin为例介绍了在部署Web服务的过程中可能遇到的问题及其解决方法，其中并未讨论Web服务的安全性问题。对于如何高效地开发和部署Web服务以及如何确保Web服务的安全性，还希望对此感兴趣的读者共同参与讨论，对于文中存在的错误和不足之处也希望读者朋友们能够不吝批评指正。 </P><!-- RESOURCES--><img src ="http://www.blogjava.net/Victor/aggbug/1471.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Victor/" target="_blank">Victor</a> 2005-02-24 10:19 <a href="http://www.blogjava.net/Victor/articles/1471.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>