您的位置:首页 > 运维架构 > Apache

使用 Apache Axis 和 Castor 创建 Web 服务

2008-09-02 22:45 411 查看
本文将展示如何利用CastorXML绑定使ApacheAxis环境中的文档方式Web服务更简单、更清晰、更直观。本文首先讨论了Web服务编码方法,并且解释了为什么Castor和Axis共同构成了一个好的解决方案。本文为创建和运行文档方式Web服务的所有步骤——从设计schema和服务到生成服务和客户端代码——提供了说明和解释。本文讲解了如何配置Axis来使用Castor,并且介绍了开发人员在解决棘手的问题时可能遇到的“局限性(gotcha)”。文档方式的正反两面 开发人员在开发Web服务时遇到的一项挑战是存在两种调用模型:RPC和文档方式。有几篇好文章详细地论述了这两种模型之间的区别,但是下面的部分将简要地介绍这些不同之处,以给本文的其余部分作一个铺垫。RPC方式编码之所以看起来有吸引力,是因为从概念上讲,它与开发人员已经使用了多年的其他实现架构(如CORBA或RMI)是相同的。文档方式引起了人们的兴趣,但是RPC比较容易并且显示出技术相当简单,正因如此,文档方式经常受到冷遇。然而,过去的经验证明,快速开发是一项技术的关键,需要立即通过网络发送真实复杂的对象。为了进行显示,发送字符串、整数抑或数组就可以了,但是现实世界使用复杂的数据结构和模型来编码数据。为了处理这种情况,SOAPRPC实现支持复杂对象序列化和反序列化。只要对象遵循JavaBean规范,就可以将对象转换成XML文档,并且采用对开发人员透明的方式进行处理。这是非常有吸引力的——用几行简单的代码就可以通过网络发送真实业务数据对象,而无需考虑基础实现。
RPC的优点在一般的情况下,简单的请求/响应更易于使用,比如代表开发人员执行从XML到方法签名的映射文档方式的优点更丰富、更完整的数据描述更大的灵活性更好地适合工作流和异步处理通常更快速与现有的XML表示的互操作共同的制止因素解析器开发(在DOM或SAX中) 从接收的文档到Java值对象的自定义、冗长的内部映射
但是反过来说,通过RPC使用复杂对象也有缺点。这种方法常常产生集成问题。一种实现的序列化可能与另一种实现的反序列化不相匹配,因为用于XMLSOAP编码过程的JavaBean的定义不明确。开放技术突然暴露出一个大的缺陷——ApacheSOAP在处理.NET时遇到麻烦,原因在于它们的实现之间的差异,因而迫切需要更加开放的服务。文档方式很好地融合了定义明确的结构和互操作性。这是通过用于定义复杂对象的标准XML-Schema实现的。与SOAP编码比较起来,XML-Schema是一种严格而容易理解的标准,可以用于定义结构。XML-Schema在定义复杂结构方面具有很大的灵活性,并且同时确保了Web服务的所有承诺——语言、平台、环境和传输中立(在使用通用应用程序编程接口的情况下)。文档方式获得了XML-Schema的所有优点,看来似乎能够解决所有的Web服务难题。然而,开发人员在选择文档方式时必须进行权衡。文档方式的一个不利的方面是增加了复杂性。开发人员会意外地发现,需要进行艰难的工作来解析XML文档和执行必要的转换,以载入其他数据bean和带有输入数据的方法请求。这对于服务器和客户端来说都是适用的。它意味着编写自定义SAX处理器,而使用和维护SAX处理器都特别困难。正在发展的解决方案:ApacheAxisApacheAxis是最流行的Web服务工具包之一。Axis支持RPC和文档方式服务,因此,它看来似乎是文档方式服务的真正开端。在使用文档方式服务时,您仍然需要以某种方式处理输入的XML数据。Axis提供了一个方便的工具来帮助完成此项艰苦的工作,这个工具名为WSDL2Java。WSDL2Java不仅可以为您的方法生成客户机和服务器的代码存根,而且可以生成实际bean,来对用XML-Schema定义的模型中的数据进行建模。然后,WSDL2Java将从XML中自动载入这些bean。通常这个过程称为数据绑定,它是XML-Schema背后的运动的支柱。WSDL2Java显得有一些特异,但是往往它帮助事情的进展。很明显,这样的一个工具是非常有用的,但不幸的是,它并不是客户端存根生成的终结技术。它还面临着一些实际的问题:WSDL2Java陷入了大多数技术所遭遇的陷阱,也就是利用schema支持迎头赶上(playingcatch-upwithschemasuppor)。编写一个工具,让它可以正确而完整地处理这些非常复杂的XML-Schema标准并不是一件小事情。就其本身而言,这代表了和Axis同样困难的工作。WSDL2Java在这方面显得不够,有缺陷,并且没有对许多XML-Schem特征提供完全的支持。例如对属性组和选项组的支持。但是这些地方正在改变,因为对WSDL2Java所做的工作也在继续。然而,编写和维护如此复杂的代码并不是Axis所看重的,并且,WSDL2Java将继续推进迎头赶上的策略,以满足独立数据绑定解决方案的功能。另外一个问题和第一个问题有关。WSDL2Java生成的代码缺少XML验证(lacksXMLvalidation)能力。当您开始使用XML文档的时候,验证是非常重要的,而且XML-Schema允许验证过程自动发生。然而,WSDL2Java还没有一个机制来完成这一工作。字符串的序列化和反序列化接口不是直观的(notintuitive)。如果您的XML对象使用的是WSDL2Java的数据绑定代码,选择就是,您将需要在某个时候将对象编码或者解码为XML字符串。虽然这对WSDL2Java的生成对象来说是可行的,但却并不是一件容易的事情。需要安装一个庞大的框架,并且有可能出现许多头疼的事情,虽然它看起来好象是一项简单的任务。开发社区对使用ApacheAxis有一些复杂的反映和困惑,还有另外一个原因:开发社区是从ApacheAxis的beta测试阶段开始伴随它一起壮大的,经历了ApacheAxis发展的整个过程。需要了解的莫名其妙的工作区和配置机制随着版本的更新而不断变化。这就是开发代码主体的现实。最初,当Axis一开始出现,重点关注的是RPCWeb服务。因为这个原因,对RPC服务的支持是最多的,并且它的接口对于开发人员来说更稳定同时也更有名。直到现在,文档方式的文档、示例、Axis的样本配置都是有限的。随着多种使用文档方式的内部配置选项(如包、文档或者消息)的出现,开发人员在建立它们自己的文档方式的服务时要经历更多的决策和复杂性。然而,这些配置方面的困难并未形成大的障碍,无论如何,使用RPC仍然是调用Web服务的一种快速而便利的方法。当然,Axis还在不断的发展,这个困难将自动解决。不过,下面我们将一步一步地讲解如何使用文档方式的服务,您将会发现这并不很难。Axis和Castor:皆为最优Castor是一个独立的XML数据绑定包,提供了从XML-Schema到Java对象的映射。这些Java对象看起来很像bean,但是可以编组(marshal)或者解组(un-marshal)为XML字符或者流,更重要的是可以对原始的Shcema进行验证。Castor是一个开放源码的工作,遵循了开放的Web服务技术。这得到了一个非常活跃的开发组织的支持,因此,产生了大量的信息和Web内容,使得开发人员可以更有效的利用这些技术。我们在ApacheAxis和WSDL不足的地方用Castor来构建所有级别的最优解决方案。我们将获得诸多好处,直观的数据绑定接口,更完全支持的schema实现,同时还利用了Axis框架的所有Web服务能力。图1显示了Axis和Castor之间的关系。
必备知识 本文的读者是熟悉Web服务和相关的创建和部署技术的媒介Java开发人员。如果您可以编写基本的WSDL并使用Axis来部署一个服务,那么您就具备了阅读本文的预备知识。同样,本文假定读者掌握XML和Schema的知识。所有的代码都是使用WebSphereStudioApplicationDeveloper和WebSphereApplicationServerversions4.0.4、4.0.6来开发、测试和部署的。也可以很轻松地用其他的开发和部署环境、库来进行替换,并且能获得同样的效果,但是,在本文中我们集中于这些环境。获得最新的Axis和Castor要构建和运行这个项目,您需要有ApacheAxis和Exolab的Castor。从http://www.castor.org可以很容易地得到Castor以及下载和安装指南。要获得ApacheAxis相对来说难度更大一些,但也不难到哪儿去。在写作本文时,使Axis和Castor可以互操作的代码还不在发布的beta版本中,只能在CVS中得到。到读者阅读本文时,这一功能将很可能已经在最新发布的JAR中了。然而,在此之前,要获得最新版本的Axis以及这个项目所需的org.apache.axis.ser.CastorSerializer和Deserializer代码,最好的方法是从CVS获取,或者下载一个最新测试版(nightlybuild),这两种方式在http://ws.apache.org/axis/cvs.html上都有。按照这里介绍的方法来下载和构建一个ApacheAxis的副本,比想象中的会更容易一些,这要感谢ApacheAnt的构建架构。在WebSphere中启用Axis这个项目的样本环境是WebSphereApplicationServerV4.0.4。在WebSphere或者其他Java容器安装Axis不会太难,但是正确地执行这些步骤仍然很重要。关于如何进行操作的指南在ApacheAxis文档中(请参阅参考信息),您也可以在本地CVS检验版(checkout)或最新测试版(nightly)中的
/xml-axis/java/docs/install.html
中找到。如果您使用WebSphereApplicationStudioV5.0,您可能会遇到一个“局限性”,这个局限性是关于WebSphereApplicationDeveloperV5上安装的环境和您试图与ApacheAxis一起使用的环境之间的JAR冲突的。如果您遇到了这些问题,可以尝试这个快速修复(quickfix):在测试服务器的服务器配置中,将环境选项面板(EnvironmentOptions)系统属性(-Dproperty)的“com.ibm.ws.classloader.warDelegationMode”设置为false。操作步骤如下:Serverperspective>theserveryouareusingforourexample>EnvironmentOptions>SystemProperties>Add....
定义Schema和服务 要开发Web服务,您需要有一个Schema,它表示两个部分,一个部分是您将要使用的数据,另一个部分是描述您将要公开的方法的的服务描述。讲解如何编写一个XML-Schema或者WSDL不在本文的讨论范围之内,不过在别的地方可以找到关于如何完成这一工作的文档。为了达到本文的目标,我们将创建一个示例服务:StockQuote服务。为您的数据定义Schema:StockQuote.xsd您的服务所需要的第一件事情就是用XML-Schema描述您的数据模型。在清单1中展示了将用在StockQuote服务中的简单数据模型,它应该是自解释的。清单1.StockQuote服务的数据模型StockQuote.xsd
<xsd:elementname="quote">
<xsd:complexType>
<xsd:sequence>
<xsd:elementname="symbol"type="xsd:string"/>
<xsd:elementname="volume"type="xsd:integer"/>
<xsd:elementname="lastTrade"type="lastTradeType"/>
<xsd:elementname="change"type="changeType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

<xsd:complexTypename="changeType">
<xsd:sequence>
<xsd:elementname="dollar"type="xsd:float"/>
<xsd:elementname="percent"type="xsd:float"/>
<xsd:elementname="positive"type="xsd:boolean"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexTypename="lastTradeType">
<xsd:sequence>
<xsd:elementname="price"type="xsd:float"/>
<xsd:elementname="date"type="xsd:long"/>
</xsd:sequence>
</xsd:complexType>
除了定义元素和complexTypes来代表您的数据模型之外,您还必须定义接口的输入/输出。这些就是Web服务将公开的方法,并且将在WSDL(下一步讲解如何创建WSDL)中进行指出,如清单2所示:清单2.StockQuote.xsd的StockQuote服务的方法签名
<xsd:elementname="getStockQuote">
<xsd:complexType>
<xsd:sequence>
<xsd:elementname="symbol"type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:elementname="getStockQuoteResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:elementref="quote"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
为您的服务定义WSDL:StockQuoteWSDL接下来,您必须为您的Web服务创建一个WSDL文件。您可以采用标准的方式来完成这一工作,但是这里有几个地方需要重点关注。首先,注意清单3中用黑体突出显示的部分。在标准方式中,您是用与您的数据模型相关联的URI来定义“types”命名空间前缀。其次,您将所需的独立Schema文件(StockQuote.xsd)导入WSDL。最后,您在WSDL的消息声明中使用了
“types”
命名空间。为什么做这些工作显得很重要?因为Castor天生就不支持在文件中嵌入
<xsd:schema>
节点,在本例中即为WSDL的
<types>
部分中的
<xsd:schema> 
节点。我们发现,像这样使用外部文件来定义您的数据模型和方法,不仅清晰、易于维护,而且更重要的是,它容许Castor和其他工具直接与您的文件形式的Schema进行交互。清单3.StockQuote服务的WSDL文件
<definitionstargetNamespace="http://w3.ibm.com/schemas/services/2002/11/15/stockquote/wsdl"xmlns="http://schemas.xmlsoap.org/wsdl/"xmlns:tns="http://w3.ibm.com/schemas/services/2002/11/15/stockquote/wsdl"xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"xmlns:types="http://w3.ibm.com/schemas/services/2002/11/15/stockquote"><types><xsd:schemaelementFormDefault="qualified"targetNamespace="http://w3.ibm.com/schemas/services/2002/11/15/stockquote/wsdl/importtypes"><importnamespace="http://w3.ibm.com/schemas/services/2002/11/15/stockquote"location="StockQuote.xsd"/></xsd:schema></types><messagename="getStockQuoteReq"><partname="parameters"element="types:getStockQuote"/></message><messagename="getStockQuoteResp"><partname="parameters"element="types:getStockQuoteResponse"/></message><portTypename="StockQuotePortType"><operationname="getStockQuote"><inputmessage="tns:getStockQuoteReq"/><outputmessage="tns:getStockQuoteResp"/></operation></portType>...
这里惟一要提请注意的事情就是将WSDL设置为使用文档方式编码。注意清单4中用粗体突出显示的部分,它是WSDL的继续。这些部分将告诉WSDL以及WSDL2Java,您使用的是文档方式的Web服务,并且您将生成文档方式(非RPC)的服务请求。清单4.StockQuote服务的WSDL文件(续)
<bindingname="StockQuoteSOAPBinding"type="tns:StockQuotePortType"><soap:bindingstyle="document"transport="http://schemas.xmlsoap.org/soap/http"/><operationname="getStockQuote"><soap:operationstyle="document"soapAction="getStockQuote"/><input><soap:bodyuse="literal"/></input><output><soap:bodyuse="literal"/></output></operation></binding><servicename="StockQuoteService"><portname="StockQuoteSOAPPort"binding="tns:StockQuoteSOAPBinding"><soap:addresslocation="http://localhost:9081/AxisWeb/services/StockQuoteSOAPPort"/></port></service></definitions>
生成所需的代码和存根 既然您已经准备好了您的服务定义和Schema,这样就可以着手于Axis和Castor。Axis和Castor都需要生成一些代码,并且您可以把Castor生成的代码放在Axis生成的代码之上,这样就就可以做到“两全其美”了,既采用了Axis的Web服务客户端和服务器代码,有带有Castor的数据绑定。这第一个部分,将显示如何生成这些代码,下一个步骤将演示如何重新配置生成的映射,这样可以获得所期望的互操作性。使用WSDL2Java构建客户端和服务器端存根WSDL2Java生成Java类(用于数据模型中的对象的数据绑定)、客户端和服务器存根代码(用于连接您的方法)、以及服务绑定信息(用于Axis服务器)。我们将继续使用后面的两个部分:存根和服务绑定,而用Castor生成的代码来替代数据模型代码。但是要完成这一工作,首先必须运行WSDL2Java来生成我们将需要的存根和服务绑定。要使用WSDL2Java生成所需要的文件,可以运行以下命令:
%javaorg.apache.axis.wsdl.WSDL2Java-s"WebContent/StockQuoteService.wsdl"--NStoPkghttp://w3.ibm.com/schemas/services/2002/11/15/stockquote/wsdl=com.ibm.w3.services.stockquote--NStoPkghttp://w3.ibm.com/schemas/services/2002/11/15/stockquote=com.ibm.w3.services.stockquote-o"JavaSource"
上面的命令将生成如清单5所示的文件。清单5.WSDL2Java生成的文件
StockQuoteSOAPBindingImpl.java//aserver-sideserviceimplementationtemplateclass,StockQuotePortType.java//aserverportTypeinterface,StockQuoteSOAPBindingStub.java//aserverstubclass,wherewe'llwritethegetStockQuotemethod,StockQuoteService.java//aclient-sideserviceinterface,StockQuoteServiceLocator.java//aclient-sideserviceimplementation(thelocator),deploy.wsdd//theservicebindingsfortheAxisserver,undeploy.wsdd_quote.java//plustheclassestorepresentthedatamodel,ChangeType.java//whichwewillbereplacingwiththeonesgeneratedLastTradeType.java//byCastor....
当您在WSDL2Java中使用命令来生成文件时,您提供了几个参数。
NStoPkg
参数指定了Java包,用于您的StockQuote.xsd文件的不同的命名空间,这将被WSDL2Java自动从WSDL文件加入进来。下一步,当您运行Castor来生成数据模型类,您可以使用同样的映射。对于WSDL2Java还有一些可选的额外参数,在本文中没有讲述,那将不使用wrapped的方式。这一个选项可以通过添加“-W”命令行来指定。在WSDL2Java中,wrapped形式意味着什么呢?它控制了WSDL2Java是如何生成方法客户端和存根。对于一个文档形式的服务,通过-W选项来指定不使用wrapped方式,这将映射到一个方法,类似于下面用StockQuoteSOAPBindingStub类生成的:
publicGetStockQuoteResponsegetStockQuote(GetStockQuotegsq)
换句话说,在StockQuote.xsd文件中定义的整个
<GetStockQuote>
元素,将作为一个单独的bean传递给您的方法,在bean中有三个字段,如清单2所示。另一方面,对于一个标准的wrapped方式的服务(也就是在这个示例中生成的),它将映射到如下的一个方法:
publicQuotegetStockQuote(Stringsymbol)
本文中将使用wrapped形式,这种形式让我们觉得更易于阅读,这样也可以免于书写额外的代码。使用Castor生成数据绑定目前,我们可以继续我们的工作,使用Castor生成数据模型绑定,来替换WSDL2Java所创建的。因为Castor不是特定于Web服务的,它是直接使用XSD文件,而不是WSDL文件。因此,可以使用下面的命令,将XSD文件传递给它:
%javaorg.exolab.castor.builder.SourceGenerator-i"WebContent/StockQuote.xsd"-packagecom.ibm.w3.services.stockquote-nomarshall-dest"JavaSource"-f
您使用命令行,并传递参数给Castor。参数
-package
是对应于WSDL2Java的NStoPkg参数。参数
-nomarshall
告诉Castor不要在所生成的bean中产生编组(marshall)方法(marshall、unmarshall、validate)。在您的示例中,这些方法是不必要的,因为CastorSerializerandDeserializerforAxis是直接使用CastorMarshaller和Unmarshaller类,这可以实现同样的目的,所以,我们使用参数来关闭它。如果您计划使用这些对象作为一个大系统的一部分,也可以打开这个参数。参数
-f
仅仅告诉Castor,在没有提示的前提下去覆盖所有现有的文件。如果您注意到了这一点,您将清楚,这将覆盖那些在前面步骤中由WSDL2Java所生成的一些数据模型bean。但是不用担心,因为您不用这些文件,所以这将不是一个问题。Castor其余的选项在castorbuilder.properties文件中指定。大部分属性都不重要,但是,有一个很重要的设置:
javaclassmapping
设置。
#Javaclassmappingof<xsd:element>'sand<xsd:complexType>'s#org.exolab.castor.builder.javaclassmapping=element
这个设置决定了Castor如何从您的Schema中生成类。根据您如何编写您的Schema,您也许想使用
element
或者
type
。两者的区别是
element
方法为所有的
complexType
类型的元素生成类。对于所有的顶级
<complexTypes>
会生成抽象类。顶级
<complexType>
类型的Schema中的元素都将为之生成一个类,这个类继承了
<complexType's>
抽象类。对于
<simpleType>
类型的元素将不生成类。然而,可以直接使用基本Java类型。另外一个选项,
type
方法的行为是不一样的。并不为没一个元素生成类,来扩展为
<complexTypes>
创建的类,
type
选项将为所有的顶级
<complexTypes>
类型和所有的内联(inlined)anonymous
<complexTypes>
创建类。元素将格式化为这些类的实例,而没有它们本身的类。这两个选项解释起来有一些复杂,但是,根据您的Schema是如何定义的,一个选项将是每一个Schema的基本选项。因此,对您自己的Schema,要先试验一下两种方法,看您更倾向于那一个类。对于这个StockQuote.xsd示例,我们发现
element
方式工作得更好,所以,在这里我们使用这个方法来生成实例数据模型bean。
配置Axis和Castor来一起工作 现在我们可以来了解事情的本质了:让Axis去使用Castor生成的类,而不是使用它自己生成的类。这个工作是本文的最关键之处,在本文中描述了如何完成这个“艰巨”的工作,在客户端和服务器端代码存根中Axis可以使用Castor的类。但是不要担心这个难题——学习完本文的一步一步的详细指导,这个工作就不再困难了。要完成这一工作,需要修改Axis服务器端存根类、以及Axis生成的.wsdd文件。修改服务器端存根去使用Castor类要让存根使用Castor类,您只需修改存根文件中的类名就可以了。正如前面我们提到的一样,尽管通常Castor和WSDL2Java将生成同样的类名,导致了同样类名的存根,但是并不是所有情况都会如此。因此,检查WSDL客户端和服务器端存根代码,确保已经设置为返回和Castor生成对象同样的正确类名。请再看清单5,可以看到两个文件,
StockQuotePortType.java
StockQuoteSOAPBindingStub.java
显示为用于服务器。为了实现目的,我们必须修改这两个文件。必要的惟一修改就是检查所有的类名,确保它们已经指向了Castor生成的类,而不是WSDL2Java生成的类。在本实例中,只有一个类名发生了改变:
_quote
。这个WSDL2Java生成的类
_quote
,在Castor中名称改成了
Quote
。它们仍然在同一个包中,正如我们生成这两个类的情形一样。但是我们必须在引用它们的地方修改类的名字。然后,文件
StockQuotePortType.java
,的下面一行
publiccom.ibm.w3.services.stockquote._quotegetStockQuote(java.lang.Stringsymbol)throwsjava.rmi.RemoteException;
改变成:
publiccom.ibm.w3.services.stockquote.QuotegetStockQuote(java.lang.Stringsymbol)throwsjava.rmi.RemoteException;
对于文件
StockQuoteSOAPBindingStub.java
,我们必须更改同一行,
publiccom.ibm.w3.services.stockquote._quotegetStockQuote(java.lang.Stringsymbol)throwsjava.rmi.RemoteException{...
改变为
publiccom.ibm.w3.services.stockquote.QuotegetStockQuote(java.lang.Stringsymbol)throwsjava.rmi.RemoteException{...
现在,所有的服务器端代码引用都指向了Castor类。这意味着,我们就可以修改deploy.wsdd文件去使用这些类和Castor配置。修改和部署server-config.wsdd文件第二个任务是修改deploy.wsdd文件。下面先让我们概览一下这个文件,需要修改的部分已经着重显示,如清单6。清单6.修改StockQuote服务的deploy.wsdd文件
<servicename="StockQuoteSOAPPort"provider="java:RPC"style="wrapped"use="literal"><parametername="wsdlTargetNamespace"value="http://w3.ibm.com/schemas/services/2002/11/15/stockquote/wsdl"/>...<typeMappingxmlns:ns="http://w3.ibm.com/schemas/services/2002/11/15/stockquote"qname="ns:changeType"type="java:com.ibm.w3.services.stockquote.ChangeType"serializer="org.apache.axis.encoding.ser.castor.CastorSerializerFactory"deserializer="org.apache.axis.encoding.ser.castor.CastorDeserializerFactory"encodingStyle=""/><typeMappingxmlns:ns="http://w3.ibm.com/schemas/services/2002/11/15/stockquote"qname="ns:lastTradeType"type="java:com.ibm.w3.services.stockquote.LastTradeType"serializer="org.apache.axis.encoding.ser.castor.CastorSerializerFactory"deserializer="org.apache.axis.encoding.ser.castor.CastorDeserializerFactory"encodingStyle=""/><typeMappingxmlns:ns="http://w3.ibm.com/schemas/services/2002/11/15/stockquote"qname="ns:quote"type="java:com.ibm.w3.services.stockquote.Quote"serializer="org.apache.axis.encoding.ser.castor.CastorSerializerFactory"deserializer="org.apache.axis.encoding.ser.castor.CastorDeserializerFactory"encodingStyle=""/></service>
对于这个文件我们要做两种类型的更改,以及还需要警惕的“局限性”。所有的更改都是针对
<typeMapping>
元素。第一个更改是修改
type
属性下面的类名,使用Castor生成的类名,而不是WSDL2Java所生成的类名,如果这两个名字有差别的情况下就需要进行这个更改。在我们的范例中,对于最开始的两个映射,
ns:changeType
ns:lastTradeType
,和Castor中的类名是一样的,所以没有必要进行更改。但是,对于
ns:quote
类,类名需要进行更改,将WSDL2Java生成的
_quote
类名更改为Castor生成的
Quote
类名。第二个进行的更改是:在
<typeMappings>
中将所有的serializers和deserializers分别的更改为
org.apache.axis.encoding.ser.castor.CastorSerializerFactory
org.apache.axis.encoding.ser.castor.CastorDeserializerFactory
。这就告诉Axis使用Castor专有的类去序列化和反序列化这些类型的引入的XML文件,而不是缺省的
org.apache.axis.encoding.ser.BeanSerializerFactory
org.apache.axis.encoding.ser.BeanDeserializerFactory
类。这些类包含在最新版本Axis1.1CVS修订中的类,也正是为什么我们需要在“获得最新的Axis和Castor”步骤中获取Axis的CVS版本。这里有一个需要提请注意的“局限性”,我们将着重强调一个地方也就是我们需要注意的。这个“局限性”必须处理Axis和WSDL2Java阅读和编写XML的方式。在我们的测试中,我们发现WSDL2Java偶尔生成无效的XML,原因是在输出中错误的防止了一些“<”和“>”。在最终的
<typeMapping>
元素
qname
属性中就是这样的一个地方。生成的
qname
属性的值是“
ns:>quote
”,很明显并不是有效的XML,因为“>”是受保护的字符。在这种情况下,删除这个多余的“>”就能解决所有的问题,并且要盯住类似于上面情况的多余字符。使用deploy.wsdd文件来部署服务既然您已经更改了您的deploy.wsdd文件,而且做好了充分的准备,这时需要把它部署到服务器上。您可以通过从deploy.wsdd文件中复制
<service>
元素到
WEB-INF/server-config.wsdd
文件,就可以完成这个工作。但是,您也可以使用Axis的便利部署工具来为您自己完成这一个工作。这个方法也确保了
server-config.wsdd
文件已经存在,如果不存在的话就创建这个文件,然后进行错误检查,确保这一方法的正确性。为了运行这个部署工具,确保您的工作目录已经设置为Axis的根目录,在该实例中是
WEB-INF
,然后运行下面的命令:
%javaorg.apache.axis.utils.Adminserverclasses/com/ibm/w3/services/stockquote/deploy.wsdd
如果这个命令得到了正确运行,将不返回任何错误,并且您的服务已经得到了部署。现在,简单的启动您的服务器,然后可以进行下一步工作。同样,如果您想在后面的某一个步骤卸载您的测试服务,运行下列命令:
%javaorg.apache.axis.utils.Adminserverclasses/com/ibm/w3/services/stockquote/undeploy.wsdd
在Axis上测试您的服务现在您已经构建了所有的相关代码,集成Axis和Castor,然后在.wsdd文件中部署服务。这时候,就可以进行测试了。通过指向您在.wsdd文件中定义的端点,您可以测试您安装的应用,在本实例中,是指向
<contextroot>/services/StockQuoteSOAPPort
。您可以看到结果如图2所示。 如果您不能得到这个显示信息,这说明在Axis中安装StockQuote服务出现了错误,这时重新查看一下您的配置以及前面的步骤,看到底是什么地方出现了问题。如果显示了这个消息,这时候您就可以编写一些代码来让
getStockQuote
方法做一些实际的工作。
编写方法 现在,什么都准备好了,但是您的服务器仍然不能做任何事情——您还没有实现
getStockQuote
方法。由于相应的事情都已经做好,要进行着一个工作就非常简单了。您只需要在
StockQuoteSOAPBindingImpl
中填写您的方法,该方法和在清单2中的方法一一对应。编写getStockQuote方法因为这是一个简单的Web服务,在这一步骤中我们不用做太多的工作。但是该样本代码说明了使用Castor生成的数据绑定代码以及由Axis来填充是非常容易的。下面是我们的
getStockQuote
方法的样本代码,见清单7。清单7.StockQuoteSOAPBindingImpl.java中的样本getStockQuote方法
publicclassStockQuoteSOAPBindingImplimplementscom.ibm.w3.services.stockquote.StockQuotePortType{publiccom.ibm.w3.services.stockquote.QuotegetStockQuote(java.lang.Stringsymbol)throwsjava.rmi.RemoteException{Quotequote=newQuote();quote.setVolume(979012312);quote.setSymbol(symbol);Changechange=newChange();change.setDollar(678F);change.setPercent(300F);change.setPositive(true);quote.setChange(change);LastTradelastTrade=newLastTrade();lastTrade.setDate(newjava.util.Date().getTime());lastTrade.setPrice(678F);quote.setLastTrade(lastTrade);returnquote;}}
这里需要着重注意的是:在这里使用的对象和在客户端使用的对象是一样的,并且已经由Castor根据它的Schema进行了验证,所以这些导入的数据可以安全的使用。如果导入的数据没有得到验证,在调用您的方法之前Castor会抛出一个异常。同样,对于验证您返回的数据这一机制将带来一些益处,并且,如果您返回的数据没有得到验证,当Axis试图序列化输出的bean的时候,Castor将抛出一个异常。另外,本例中尽管我们的bean非常简单——只有
Quote、Change
LastTrade
对象——Castor处理这些复杂的XML-Schema数据模型非常完美,但是WSDL2Java将会遇到问题。
构建客户端 既然您已经成功的在服务器端完成了运行Axis和Castor的所有工作,现在就可以构建一个客户端了。使用动态的AxisWeb服务客户端以及我们的Castor知识,我们将完成如下所示的一个初步工作,如清单8。清单8.构建StockQuote服务的客户端的初步工作
Serviceservice=newService();Callcall=(Call)service.createCall();call.setTargetEndpointAddress(newURL(ENDPOINT));GetStockQuoterequest=newGetStockQuote();request.setSymbol("IBM");StringWritersw=newStringWriter();Marshallermar=newMarshaller(sw);mar.marshal(request);Stringxml=sw.getBuffer().toString();Documentdoc=XMLUtils.newDocument(newByteArrayInputStream(xml.getBytes()));SOAPBodyElement[]input=newSOAPBodyElement[1];input[0]=newSOAPBodyElement(doc.getDocumentElement());Vectorelems=(Vector)call.invoke(input);
这样就可以了,但是并不是这么自动化。然而,对Axis生成的客户端存根使用一些巧妙的办法,我们可以做得更好,可以有一个客户端可以自动化的完成所有的安装过程。Tomake为了让存根使用Castor类,简单的修改存根文件中的类名,就象和服务器存根中一样——虽然,通常Castor和WSDL2Java生成同样的类名,导致存根也是有同样的类名。但并不是所有情况都这样,所以检查和更正显得很重要。您需要修改的文件是
StockQuoteSOAPBindingStub.java
。在本实例中,WSDL2Java生成了用于Quote元素的类,名为
_quote
,但是Castor生成的名字为
Quote
,所以您需要在所有的地方进行更改。在这里并不列出所有的引用了这个类的地方,以及每一行需要更改,这是一个重复性的事情,并且对于阅读没有什么益处,我们将把这个工作交给您来做,仅仅需要浏览该文件,将所有对类
com.ibm.w3.services.stockquote._quote
的引用更改为
com.ibm.w3.services.stockquote.Quote
。更改客户端,使用Castorserializers代替Axisserializers目前,我们进行关键的更改:修改客户端去使用Castor专有的serializer以及deserializer类,来代替缺省情况下WSDL生成的BeanSerializers。在服务器端,我们可以修改配置文件,
deploy.wsdd
,设置使用哪个类来序列化和反序列化。不幸的是,客户端并不使用这样的一个配置文件,所以我们必须在代码中修改。然而,这是一个非常简单的修改,并且可以很清楚地看出,它和在服务器端对
deploy.wsdd
的修改是一样的。要Castorserializer和deserializer类,您需要做的第一件事情就是将它们添加到可能的序列器(Serializer)类列表中(在
StockQuoteSOAPBindingStub.java
文件的构造函数
publicStockQuoteSOAPBindingStub(javax.xml.rpc.Serviceservice)
中创建)。清单9.更改StockQuoteSOAPBindingStub.java中的构造函数
publicStockQuoteSOAPBindingStub(javax.xml.rpc.Serviceservice)throwsorg.apache.axis.AxisFault{if(service==null){super.service=neworg.apache.axis.client.Service();}else{super.service=service;}java.lang.Classcls;javax.xml.namespace.QNameqName;java.lang.Classcastorsf=org.apache.axis.encoding.ser.castor.CastorSerializerFactory.class;java.lang.Classcastordf=org.apache.axis.encoding.ser.castor.CastorDeserializerFactory.class;java.lang.Classbeansf=org.apache.axis.encoding.ser.BeanSerializerFactory.class;java.lang.Classbeandf=org.apache.axis.encoding.ser.BeanDeserializerFactory.class;java.lang.Classenumsf=org.apache.axis.encoding.ser.EnumSerializerFactory.class;java.lang.Classenumdf=org.apache.axis.encoding.ser.EnumDeserializerFactory.class;java.lang.Classarraysf=org.apache.axis.encoding.ser.ArraySerializerFactory.class;java.lang.Classarraydf=org.apache.axis.encoding.ser.ArrayDeserializerFactory.class;java.lang.Classsimplesf=org.apache.axis.encoding.ser.SimpleSerializerFactory.class;java.lang.Classsimpledf=org.apache.axis.encoding.ser.SimpleDeserializerFactory.class;...
清单9显示了用粗体显示的构造函数的修改。这个修改是自描述的:它添加了
CastorSerializerFactory.class
CastorDeserializerFactory.class
作为后面代码可能使用的选项,定义了用于输入和输出对象的序列器(serializer)和反序列器(deserializers)。更新客户端的最后一个步骤是:在上面提到的构造函数中,更改所有
cachedSerFactories.add(beansf)
cachedDeserFactories.add(beandf)
出现的地方,用Castor的BeanSerializer 代替Axis的BeanSerializer。这等同于我们对
deploy.wsdd
的更改,这里我们列出了Castor序列化和反序列化类,而不是Axis的BeanSerializer。在用到您的对象时,将每一个
beansf
beandf
的地方更改为
castorsf
castordf
。在所有出现
beansf
beandf
的地方,您都需要将它们改为
castorsf
castordf
,在本例中,这两者都出现了。例如,如清单10所示的一个代码块的更改,其修改用粗体表示:清单10.在StockQuoteSOAPBindingStub.java中更改的代码块
qName=newjavax.xml.namespace.QName("http://w3.ibm.com/schemas/services/2002/11/15/stockquote","lastTradeType");cachedSerQNames.add(qName);cls=com.ibm.w3.services.stockquote.LastTradeType.class;cachedSerClasses.add(cls);cachedSerFactories.add(castorsf);cachedDeserFactories.add(castordf);
在构造函数中更改每一个保留的代码块,然后您完成了这个任务:Axis生成的客户端目前使用Castor序列化和数据绑定。试用生成的客户端现在,您可以编写客户端代码,如清单11。清单11.最终的StockQuote客户端
StockQuoteServiceservice=newStockQuoteServiceLocator();StockQuotePortTypeport=service.getStockQuoteSOAPPort();Quotequote=port.getStockQuote("IBM");System.out.println(quote.getVolume());
这样更安全,更加自动化,而且更容易使用。并且,现在您获得了一个Web服务,使用Axis来进行通信,使用Castor进行验证和数据绑定,这是一个端到端的情形。
Axis和Castor:文档方式 正如您在本文中所看到的,整合文档方式的服务、Castor和Axis并不是想象的那么可怕,仅仅是安装有点复杂。但是一旦您成功了,您的Web服务将具备文档方式的灵活性和清晰度,对Axis健壮的支持,以及Castor的验证和数据绑定优势。一旦您掌握了本文所讲的全部内容,还有许多有趣的方面有待您去钻研。例如,使用CastorJDO,您只需写几行代码就可以让您的服务器端Castor对象把自己编组到SQL数据库中。您也可以使用Castor的标准表达式和验证支持来清理Web服务数据,这样您的服务和客户端的数据中出现错误的可能性就减少了。要了解关于这些主题的更进一步的信息,请参阅关于整合Castor和Axis的后续文章。参考资料ReapthebenefitsofdocumentstyleWebservices逐步了解Web服务标准SubscribetothedeveloperWorksnewsletterdeveloperWorksToolboxsubscription从Castorwebsite可以得到Castor的最新副本。参阅ApacheAxis文档,它在WebSphere或者其他Java容器提供的aguideforgettingAxissetup中。AxisCVSpage包含了关于从CVS或者最新测试版本的文件中下载和构建Axis的指导。关于如何安装和使用Axis,请查看Axisinstallationinstructions文档页。“ReapthebenefitsofDocument-styleWebservices”(developerWorks,2002年4月)更加深入地解释了Castor的数据绑定框架的益处。系列文章:逐步了解Web服务标准(developerWorks,2002年10月),解释了SOAP的复杂性。开发Web服务(developerWorks,2001年11月),提供了一个关于如何为Web服务定义WSDL的介绍。使用XML-Schema定义元素的基础(developerWorks,2000年8月)将帮助您为您的服务数据定义XML-Schema。作者简介
KevinGibbs是一位软件工程师,在英国剑桥大学学习IBM的高级Internet技术,获得了MA学位。他早先从事SashXBforLinux脚本环境研究,目前正研究多种Internet技术,包括Web服务和blogging架构。您可以通过kagibbsatus.ibm.com与Kevin联系。
BrianGoodman是一位IT架构师,专门从事IBMIntranet的咨询、交流和协作。您可以通过bgoodmanatus.ibm.com与Brian联系。
EliasTorres是一位软件工程师,在英国剑桥大学学习IBM的高级Internet技术,获得了MA学位。他从事Sash脚本环境研究,探索一些技术,例如blogging和它们在公司环境的实际益处。您可以通过eliastatus.ibm.com与Elias联系。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: