XFire:开发Web服务的简易之道
时间:2008-09-01 来源:ybu2008
问过一些.NET程序员:开发Web服务有多艰苦? 他们被我的问题逗乐了! 他们回答说:用时小于1分钟.
同样的问题,我又问了一个Java程序员, 得到的回答与先前.Net程序员的大相径庭.
Web服务这个概念引入Java已经有5年了.然而Web服务还是被认为是一个较新的技术,这是Web服务在Java上难于实现的主要原因.
如今,随着新一代Web服务引擎--XFire的发布, 一切发生了翻天覆地的变化.
使用XFire,你不需要写一句代码就可以将Java类方法转换成Web服务.
阅读本文,你将领略到XFire是如何将开发Web服务变得无比轻松,简易的.
Web服务
Web服务允许我们建立应用组件放置于网络上的分布式系统. 不管这些应用组件是怎么写的,使用什么语言写的,要运行在什么操作系统上, 我们都能以统一的方式访问这些组件.如果一个Web服务有效并且设计实现了互用性问题,那么无论你的应用是用什么语言,在什么平台上开发的,都可以使用这些服务.
为了实现平台无关,实现独立的访问Web服务, 业界制定了一系列技术标准,下面是一些最重要的技术:
* XML
* SOAP
* WSDL
下图展示了上述技术在工作环境中是如何使用的.
在这里,提供者是可以提供服务的应用组件, 申请者是需要使用服务的客户端程序. 很多其他技术也参与了这个交互过程,但是这里只显示了在Web服务环境中必不可少的核心组件.
XFire
XFire是一个免费的,开源的SOAP框架. 它不仅允许你轻松简易地实现这么一个环境.而且还提供了很多先进的特性.不错,你没有看错,"轻松简易". 本文中你将会看到用XFire构建Web服务是多么的简单.如果你的Web应用有一个Java类, 现在你希望这个类编程Web服务,用XFire完成这一工作你不必写一句代码.仅需操作一下部署描述器,你就会得到一个Web服务. 是的, 就是这么简单.让我们来看个例子.
一个简单的Java类
我的这个例子是一个位于Apache Tomcat 5.5.7主机运行在J2SE 1.4.2_07下的银行应用. 我假设你已经知道如何使用Java编写web应用并且已经配置了Tomcat服务器.我们的这个应用很简单, 只完成一项工作, 就是将钱从一个帐户转到另一个帐户.BankingService类中的transferFunds()为我们完成这一工作. 他需要4个传入参数
- String fromAccount
- String toAccount
- double amount
- String currency
下面是代码:
import java.text.NumberFormat;
import java.text.DecimalFormat;
/** *//** XFire WebServices sample implementation class.
*/
public class BankingService implements IBankingService {
//Default constructor.
public BankingService(){
}
/** *//** Transfers fund from one account to another.
*/
public String transferFunds(
String fromAccount, String toAccount, double amount, String currency){
String statusMessage = "";
//Call business objects and other components to get the job done.
//Then create a status message and return.
try {
NumberFormat formatter = new DecimalFormat("###,###,###,###.00");
statusMessage = "COMPLETED: " + currency + " " + formatter.format(amount)+
" was successfully transferred from A/C# " + fromAccount + " to A/C# " + toAccount;
} catch (Exception e){
statusMessage = "BankingService.transferFunds(): EXCEPTION: " + e.toString();
}
return statusMessage;
}
}
在上面的代码中你看到奇怪的代码了吗? 大概没有吧. 除了那个公共的默认构造函数.这个构造函数是必不可少的,因为XFire要用他来实例这个类.
因为使用接口来完成是很好的实践,因此我们的类也实现了一个名叫IBankingService的接口.代码很简单:
public interface IBankingService {
public String transferFunds(
String fromAccount, String toAccount, double amount, String currency);
}
实际开发中,这个方法可能包含所有复杂的调用,请求和处理操作,但是我们的例子将其最小化以便我们能将精力集中到我们的目标上:将这个方法转换成Web服务.
你可以看到,我们的BankingService只不过是一个普通的Java类,没有任何代码说明它是否在Web服务中使用.这就对了!我们不需要写任何代码,所有工作都交给部署描述去处理.
后面我将介绍如何编写部署描述。
XFire:开发Web服务的简易之道(二)--Web应用的部署配置
摘要:XFire本身就是基于Servlet的应用,因此我们需要向文件中添加一些必要的参照.那么我们就需要配置我们创建的Web服务.我们使用名叫services.xml的文件来完成配置。
Web应用的部署描述
在Java中,Web应用至少使用一个名叫web.xml的部署描述来部署. XFire本身就是基于Severlet的应用,因此我们需要向文件中添加一些必要的参照.那么我们就需要配置我们创建的Web服务.我们使用名叫services.xml的文件来完成配置.
web.xml
首先想让我们看一下web.xml.我们需要加上如下语句:
<servlet-name>XFireServlet</servlet-name>
<display-name>XFire Servlet</display-name>
<servlet-class>org.codehaus.xfire.transport.http.XfireConfigurableServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/servlet/XFireServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
services.xml
下面我们就要描述一下我们的Web服务都包含什么.这个工作在services.xml中完成.这个文件位于META-INF/xfire目录下,下面是基本的配置条目:
<beans xmlns="http://xfire.codehaus.org/config/1.0">
<service>
<name>Banking</name>
<namespace>mybank</namespace>
<serviceClass>com.mybank.xfire.example.IBankingService</serviceClass>
<implementationClass>com.mybank.xfire.example.BankingService</implementationClass>
</service>
</beans>
让我们看看文件中的具体内容. 对Web服务的定义包含在<service>元素内.<service>元素下还有若干子元素.
第一个子元素是<name>, 你可以提供任何有效的xml名字,这个名字会被客户端程序和服务器上的其他组件使用.例如,当服务器起来以后,你可以在浏览器上使用这个名称来查看WSDL.
下一个子元素是<namespace>. 任何有效地xml名称都可以, <namespace>将作为你服务器的唯一标识变量使用.
<serviceClass>元素包含Java类名用来指明方法的签名.在我们的这个例子中是IBankingService接口.如果你的Java类没有实现任何接口,那就填入类名.在你的Java类或接口中也许含有不知一个方法,只需要一个入口来将他们转换成Web服务.
<implementationClass>元素记录实现接口的Java类名.这是一个可选元素.如果前一个元素<serviceClass>填入的是接口,那么此处就要填入相应的实现类名.
至此,我们的Web服务配置工作就完成了.
XFire和其他库
现在,我们做最后一步工作--获得所有必要的库文件.我们怎么获得他们呢? 访问XFire官方网站http://xfire.codehaus.org/ 下载xfire-distribution-1.0.zip并解压到本地文件夹中.将下列文件拷贝到WEB-INF\lib:
- commons-codec-1.3.jar
- commons-httpclient-3.0.jar
- commons-logging-1.0.4.jar
- jaxen-1.1-beta-8.jar
- jdom-1.0.jar
- log4j-1.2.x.jar
- mail-1.3.3_01.jar
- spring-1.2.x.jar
- stax-api-1.0.jar
- wsdl4j-1.5.2.jar
- wstx-asl-2.9.jar
- xbean-2.1.0.jar
- xbean-spring-2.2.jar
- xfire-all-1.0.jar
- XmlSchema-1.0.jar
大功告成!让我们部署并启动应用.要部署这个例子,只需要将websvc.war复制到Apache Tomcat 环境的webapps目录下,等待几分钟,应用会自动启动. 例子的所有源代码也包含在这个war文件中(文件太大,我将其分卷压缩)part1 part2 part3 part4 part5.现在,我们的应用已经是一个Web服务了.
我们怎么知道Web服务有效?
我们来做一些测试,看看Web服务是否有效.
首先,我们先来看看WSDL是否有效。在浏览器中输入URL。哪个URL?因为我们的war文件名叫websvc.war,services.xml 中给出的servicenames是Banking, 那么WSDL URL为http://localhost:8080/websvc/services/Banking?wsdl。
请注意:URL的前一部分,例如:http://localhost:8080会根据你安装的应用服务不同而不同。如果你输入了URL,你将会看到以<wsdl:definitions>为根结点的xml文件。这个文件叫做web服务的WSDL.如果你看到了这个文件,那么初步验证你的Web服务有效。
但是这个验证还不够。有时候情况会复杂一些,你可以看到WSDL,但是客户端却无法访问Web服务。因此要真正检验Web服务是否真的好使,就要用客户端程序对Web服务作一次真正的调用。
后面我将介绍如何开发客户端。 -
/**//* Call the Web service
*
*/
public String callWebService(
String fromAccount, String toAccount, double amount, String currency)
throws MalformedURLException, Exception
{
//Create a metadata of the service
Service serviceModel = new ObjectServiceFactory().create(IBankingService.class);
log.debug("callSoapServiceLocal(): got service model." );
//Create a proxy for the deployed service
XFire xfire = XFireFactory.newInstance().getXFire();
XFireProxyFactory factory = new XFireProxyFactory(xfire);
String serviceUrl = "http://localhost:8080/websvc/services/Banking";
IBankingService client = null;
try
{
client = (IBankingService) factory.create(serviceModel, serviceUrl);
} catch (MalformedURLException e)
{
log.error("WsClient.callWebService(): EXCEPTION: " + e.toString());
}
//Invoke the service
String serviceResponse = "";
try
{
serviceResponse = client.transferFunds(fromAccount, toAccount, amount, currency);
} catch (Exception e)
{
log.error("WsClient.callWebService(): EXCEPTION: " + e.toString());
serviceResponse = e.toString();
}
log.debug("WsClient.callWebService(): status=" + serviceResponse);
//Return the response
return serviceResponse;
}
这段代码做了些什么?让我解释一下:首先我们创建了一个服务模型,里面包含了对服务的描述,换句话说,我们创建了服务的元数据。我们是用XFire的ObjectServiceFactory通过接口IBankingService.class创建了这个服务模型。
下一步就是获取XFire的代理对象。这一步中没有任何应用细节。通过proxyFactory,使用服务模型和服务终点URL(用于获得WSDL),我们获得了服务的本地代理。
这个代理就是实际的客户端。现在我们可以调用transferFunds()方法获得我们想要的Web服务了。
一旦这个例子部署成功并启动,就可以用下面的Servlet URL检验:
http://localhost:8080/websvc/ws
Servlet使用默认的参数访问Web服务并显示接收到的响应。你应该能看到下面两行信息
Response Received
COMPLETED: CDN$ 500.00 was successfully transferred from A/C# 11111-01234 to A/C# 99999-05678
现在你可以确信Web服务真的起来了并且工作正常。
你可以尝试传入不同的数据。你可以输入类似于下面的URL
http://localhost:8080/websvc/ws?from=11-2345&to=77-9876&amt=250.00&cur=EUR.
Web服务开发的基本步骤
下面列出了使用XFire开发Web服务的基本步骤:
1、检验Java类的方法和构造函数时公共的;
2、将XFire Servlet相关的入口添加到web.xml中;
3、创建services.xml并把它放到WEB-INF/classes/META-INF/xfire目录下;
4、将Xfire和其他第三方库添加到你的Web应用的WEB-INF/lib 目录下。
OK,这就是全部要做,就是这么简单。
-----------------------------------------------------------------------
这片文章是片译文(原文在devx,具体记不得了),对于想初步了解webservice的朋友可能有些帮助。其中有一些模式的应用,不过个人觉得太简单了,忘大家多想想,发表些意见。
Webservice 作为一项新的技术出现在我们面前,它的出世是用于解决在不同的平台下的应用的协同的。目前几乎每家厂商都要去开发Webservice 应用,然而如果缺乏对Webservice更深的了解,不能很好的在设计阶段处理好一些重要的问题,那么最终完成的系统必然是效率低下,没有可靠性的产品。
在设计Webservice 应用时,以下几点务必要考虑到:
l 管理好与外系统的协同关系
l 掌握底层的传输模型
l 提供与应用相适应的安全策略
l 计划好部署的相关事项
以下,将就这几条相关的设计需求和一些常用模式是如何应用于Webservice模型展开详细讨论。在讨论中,你会发现Webservice这项新的技术是如何与我们在以往的软件开发相结合的。
l 标准提供了协同的能力
Webservice的一个最基本的目的就是提供在各个不同平台的不同应用系统的协同工作能力。
为了使得一个公司的网络应用达到最高的效率,存在它自己和它的合作伙伴,供应商以及客户之间的Webservice,应该能够实现无缝的交互。如果在众多的Webservice之间不能轻松的实现交互,那么该应用的效率将大打折扣。但是,在现实中这种情况是极有可能出现的。由于各个公司对业务的理解各不相同,就是理解相同的情况下,对于相同的概念也可能用不同的形式加以表现,具体而言就是对于同一数据可能采取不同的xml表示。由于以上的原因,对于协同性的问题应该在设计应用架构时就加以考虑,而不是留待以后去改变。
Webservice 主要由以下几块技术所构成,SOAP (Simple Object Access Protocol), WSDL (Web service Description Language), 以及UDDI (Universal Description, Discovery and Integration)。
在这里我们不会去详细研究这些技术,而是揭示他们的一些重要特性,这些特性需要在Webservice的设计时详加考虑。
WSDL是实现协同能力的关键,它提供了一份契约用于与新老的应用之间交互。这项技术使得各个组织可以将标准的制定集中在Service的外部接口,而不用考虑各组织的具体实现。简而言之,它实现了Webservice的接口与实现的分离。从而使得标准的制定,更加容易。并且,基于这份接口描述,很多工具可以从中自动生成客户端代码,减少了开发者的工作量,并使得大部分开发者摆脱了编写SOAP消息传递代码过程。
SOAP是实现在各个Webservice组件之间传递消息的传输层。因此,SOAP应该是一项透明的协同技术。但是,由于很多的SOAP实现方法却与标准背道而驰,要么添加了新的扩展功能要么删减了一些标准功能。由于对SOAP标准的支持程度不同,使得Webservice的协同能力大打折扣,实现协同的困难加大了。基于这种情况,当开发者需要Webservice运行在不同平台上时,就要对具体情况加以了解并相应的编码以解决这种不一致性。如果所有的SOAP实现组织都能够遵循标准的话,那么Webservice的开发者就不需要考虑使用该Webservice的底层平台了。
尽管如此,不同SOAP实现的协同还是相当困难,因为协同标准的制定存在大量的分歧,目前一些组织正致力于标准的制定,比如SOAP Builders 和 WS-I。然而,现在Webservice开发者只有针对不同平台,给予不同的实现,使得开发的成本和负担加大了。
l 理解传输模型
SOAP并不是完全透明的解决方案,它把一些复杂的实现细节隐藏起来。Webservice的开发者必须深入的了解SOAP,了解底层的传输机制以及模型,从而知道SOAP是如何实现的。在一些简单的应用中,某些工具可以帮助Webservice的开发者生成SOAP消息传递的代码,但是这只在最简单的应用中有效。真正的情况不可能那么简单,可能在某些方面你需要有特殊的处理(这种情况在实际开发中是很常见的),这个时候,你就需要直接操纵SOAP的消息传递代码,以及一些底层的XML内容。因此,Webservice的开发者需要深入了解SOAP和XML层的内容。
在开发Webservice的接口的时候,不要以为使用XML技术,协作性的问题就迎刃而解了,XML并不是解决集成问题的灵丹妙药。这里同样需要标准的制定,需要一个在业界公认的词汇表。仅仅在你的设计框架中引入XML技术并不能保证系统具有协同性,XML仅仅是用来描述数据的语言,XML自己并不提供语义去理解数据。就如同英语和德语都使用拉丁字母,但是他们的语义却并不相同。
即使你使用相同的语言,也不能保证具有良好的协作性。比如你的公司可能使用Order描述一个订单,但你的合作伙伴可能使用Purchase_Order,而另一个伙伴可能又不相同。你不可能强迫你所有的合作伙伴都采用和你相同的词汇。因此需要有一项技术可以在众多的描述之间充当翻译的角色。XSLT就是这么一种技术,它用于不同语言的转换。和XSLT的配合使用XML才能解决协同性的问题。
l DOM vs. SAX
许多的Webservice开发环境,将开发者从底层的XML文档的解析和处理中解放出来,他们提供了自动化或者很方便的工具,使得这一过程变得很简单。但是对于一些有特殊要求的Webservice应用,比如需要更好的柔性或者对速度要求特别高的应用,就需要手工处理XML文档。这时候两种XML解析的模型-DOM 和SAX的选择,将成为重要的问题。
DOM使用树状图的方式解析XML文档,而SAX则更多的采用事件驱动的模型。
DOM先将XML文档映射成一颗树,然后通过采用一系列与树相关的操作去处理这份文档。这种方法有很多的好处,首先开发者很容易理解,使用一颗树这对于开发者来说是最常见不过的了。DOM最常用于XML在Service中需要频繁修改的场合。当然DOM也有它的缺点,在处理XML文档的时候,它需要载入整个文档,而不管你需要修改的是否只是其中的一小部分。因此它的运行效率以及对内存的使用显然是不能接受的,尤其是面对很大的XML文档。
SAX使用事件驱动的模型来处理XML文档。通过一系列事件的触发,来完成对XML的解析,你可以只关心你所要处理的事件,当这些事件发生时,会调用到相应的回调函数来通知到你。采用这种方式就可以在很大程度上提高XML文档解析的效率。但是它的缺点在于难于使用,以及对同一文档的多次处理会存在一些问题。
总而言之,DOM更适合处理那种文档型的XML文件,而SAX则适于那种想直接将XML结构映射成在你系统中的一个对象的操作。(比如将一个XML结构直接映射成JAVA中的一个Class)或者那种针对XML文件中特殊Tag的操作。
l 文档交换vs. RPC模型
这两种交互方式应该在应用架构的设计初始就应该详加考虑,因为它将在很大程度上决定系统的耦合程度。
RPC(Remote Procedure Call)本质上就是远程方法的调用。尽管Webservice是基于XML的但是你仍然可以使用远程方法调用这种模式来进行Webservice的实现,尤其是在那种简单的请求相应的模型中。在这个过程中,传输中的XML文件所描述的更多是有关远程方法的信息,比如方法名,方法参数等等。
而文档交换方式,与RPC相比较在XML文件中不是做远程方法的映射,而是一份完整的自包含的业务文档,当Service端收到这份文档后,先进行预处理(比如词汇的翻译和映射),然后再构造出返回消息。这个构造返回消息的过程中,往往不再是简简单单的一个方法调用,而是多个对象协同完成一个事务的处理,再将结果返回。
这两种方式的区别,类似与打电话和发邮件的不同处理方法。在目前,对于第一种方法提供了很多自动化的工具使得远程方法的调用能够很容易的完成,而后一种方法缺少一系列工具的支持,需要开发者手工完成。
尽管如此,在此还是推荐使用文档交换的方式。由于它在以下方面具有RPC所不具备的优点。
使用文档方式,你可以充分利用XML文件的功能去描述和验证一份业务文档,而在RPC模型中XML仅仅被用于描述方法的信息。
使用文档方式,在客户的Service的提供者之间不再需要紧密的约定,而RPC模型需要客户和Service的提供者紧密相连,一旦方法发生变化,客户端就需要做相应的改动。这不符合低耦合系统的要求,而在文档交换方式中则灵活的多。
由于业务数据是自包含的,显然文档模型更利于采用异步处理。
l 利用设计模式
设计模式在设计Webservice的时候显然可以起到相当大的作用。设计模式的主要目的就是为解决某些在类似环境下的相像问题提供已有的较为成熟的设计方案。在这里,只简单的提及一些很常用的模式,让我们了解到模式在Webservice中可以起到的作用。
Adapter :为内部系统提供一个不同的接口
Fa?ade: 封装复杂的内部实现,提供一系列简单的接口
Proxy: 作为其他对象的代理,代替它提供服务
Adapter模式用于将一个组件的接口转化成客户所需要的样子,这里的客户就是Webservice。一个常见的情况就是将原有的老的系统包装成一个Webservice。比如现在使用的是J2EE的平台,而原来有一个C++的系统实现了某些功能,现在需要将它发布成Webservice,那么就需要利用JNI技术做一个Adapter,为原来的C++组件提供一个Java的接口,然后再转化为Webservice。
Fa?ade模式用于构建粗粒度的服务,它包装了细粒度的服务,从而为复杂的系统提供了一个简单的接口。在J2EE中,Session Bean就象是一个Fa?ade,而Entity Bean则是细粒度的服务。在Webservice中也一样,使用Fa?ade模式可以将已有的组件的功能发挥殆尽。
Proxy 模式用于充当其他对象的代理,类似于中间人的作用,将处理工作从一个对象传递到另一个对象。在Webservice中,它主要用于隐藏Soap消息构造的过程。也可以用于模拟对象(Mock Object)的创建。
以上仅仅是一些可以用于Webservice开发的模式,如果你熟练的将这些模式应用于Webservice开发,你将会发现开发Webservice应用,将好像做一种特殊的面向对象设计。
l 安全
Webservice为作为方便的服务被用广大领域使用的同时,也成为了黑客们的美食。在这里,本文将就目前对Webservice安全所能做的改进做简单介绍。
在Webservice中的安全主要分为以下三个方面。
传输 SSL/HTTPS 对连接加密,而不是传输数据
消息 数据加密(XML Encryption) 数字签名(XML-DSIG)
底层架构 利用应用服务安全机制
传输时的安全是最容易被加入到你的Webservice应用中的,利用现有的SSL 和HTTPS协议,就可以很容易的获得连接过程中的安全。
然而这种安全实现方法有两个弱点。一是它只能保证数据传输的安全,而不是数据本身的安全,数据一旦到达某地,那么就可以被任何人所查看。而在Webservice中,一份数据可能到达多个地方,而这份数据却不该被所有的接受者所查看。二是它提供的是要么全有要么全无的保护,你不能选择哪部分数据要被保护,而这种可选择性也是在Webservice中所常要用到的。
第二层的保护是对于消息本身的保护。你可以使用已有的XML安全扩展标准,实现数字签名的功能,从而保证你的消息是来自特定方并没有被修改过。XML文件的加密技术从更大程度上加强了Webservice的安全,它能够定制数据传输到后,能否被接受者所查看,进一步完善了传输后的安全,业界也在不断的制定Webservice的安全标准,比如SAML 和 WS-Security。
最后一层保护就是依靠底层架构的安全,这更多的来自于操作系统和某些中间件的保护。比如在J2EE中,主持Webservice的应用服务器。目前很多的J2EE应用服务器都支持Java Authentication and Authorization Service (JAAS),这是最近被加入到J2SE 1.4当中的。利用主持Webservice的服务器,实现一些安全机制这是很自然的做法。另一种利用底层架构的安全方法就是,做一个独立的负责安全的服务器,Webservice的使用者和创建者都需要与之取得安全信任。
XFire:开发Web服务的简易之道(三)--开发客户端程序
摘要:在《XFire:开发Web服务的简易之道(二)》中最后提到过:要想检验Web服务是否有效,需要一个客户端程序实际调用一下这个Web服务。本章我们就来创建这样一个用于检验的客户端程序,验证一下我们前面的工作是不是真的有效。最后总结一下用XFire开发Web服务的步骤。
创建客户端程序
我们可以使用很多方法,任何SOAP工具来创建客户端程序,例如.Net或Apache Axis。我的这个例子中我是用一个名叫WsClient.java的Servlet中的动态代理。为了减少编码的代价,我将所有屏幕构建元素都放到doGet()方法中。对Web服务服务的实际调用在callWebService()方法中。代码很简单: