一 Web Service 简介

1.1什么是Web Service?
从表面上看,Web service 就是一个应用程序,它向外界暴露出一个能够通过Web进行调用的API。这就是说,你能够用编程的方法通过Web来调用这个应用程序。我们把调用这个Web service 的应用程序叫做客户。
另一种更精确的解释:Web services是建立可互操作的分布式应用程序的新平台。Web service平台是一套标准,它定义了应用程序如何在Web上实现互操作性。你可以用任何你喜欢的语言,在任何你喜欢的平台上写Web service,只要我们可以通过Web service标准对这些服务进行查询和访问。Web service平台需要一套协议来实现分布式应用程序的创建。任何平台都有它的数据表示方法和类型系统。要实现互操作性,Web service平台必须提供一套标准的类型系统,用于沟通不同平台、编程语言和组件模型中的不同类型系统。在传统的分布式系统中,基于界面(interface)的平台提供了一些方法来描述界面、方法和参数(译注:如COM和COBAR中的IDL语言)。同样的,Web service平台也必须提供一种标准来描述Web service,让客户可以得到足够的信息来调用这个Web service。最后,我们还必须有一种方法来对这个Web service进行远程调用。这种方法实际是一种远程过程调用协议(RPC)。为了达到互操作性,这种RPC协议还必须与平台和编程语言无关。
1.2 Web Service用到的技术
为了实现平台无关,实现独立的访问Web服务, 业界制定了一系列技术标准,下面是一些重要的技术:
A.XML
可扩展的标记语言(XML)是Web service平台中表示数据的基本格式。它的内容与表示的分离十分理想,除了易于建立和易于分析外,XML主要的优点在于它既是平台无关的,又是厂商无关的。无关性是比技术优越性更重要的:软件厂商是不会选择一个由竞争对手所发明的技术的。
B.SOAP (Simple Object Access Propotol 的简称)
Web service建好以后,你或者其他人就会去调用它,简单对象访问协议(SOAP)提供了标准的RPC(远程过程调用协议)方法来调用Web service,SOAP规范定义了SOAP消息的格式,以及怎样通过HTTP协议来使用SOAP,SOAP也是基于XML,XML是SOAP的数据编码方式。
C.WSDL
你会怎样向别人介绍你的Web service有什么功能,以及每个函数调用时的参数呢?你可能会自己写一套文档,你甚至可能会口头上告诉需要使用你的Web service的人。这些非正式的方法至少都有一个严重的问题:当程序员坐到电脑前,想要使用你的Web service的时候,他们的工具(如Visual Studio)无法给他们提供任何帮助,因为这些工具根本就不了解你的Web service。解决方法是:用机器能阅读的方式提供一个正式的描述文档。Web service描述语言(WSDL)就是这样一个基于XML的语言,用于描述Web service及其函数、参数和返回值。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的,这将是一个很大的好处。一些最新的开发工具既能根据你的Web service生成WSDL文档,又能导入WSDL文档,生成调用相应Web service的代码。

二 Web service开发框架介绍
        
 2.1 Apache SOAP 与 AXIS,XFire框架介绍及之间的区别
Apache SOAP
Apache SOAP是SOAP协议的早期实现
如果有wsdl文件,但生成不了代码,或没有wsdl文件,这时,只能使用发送soap包,接收soap包的方式,实现Web Service接口,可以首选Apache SOAP包
实际上,采用发送SOAP包,接收SOAP包实现的接口效率,远远大于使用java方法调用的Web Service。因为系统不需要分析soap包,不需要分析xml,不需要反序列化等操作
因为需要开发人员组装SOAP包,分析SOAP包,所以对开发人员来说,是最困难的实现方式
Axis
是Apache WebService项目中的子项目,Axis本质上就是一个SOAP引擎,提供创建服务器端、客户端和网关SOAP操作的基本框架。Axis是第二代Apache SOAP
Axis2
是新一代WebService框架,Axis 的后续版本。它设计为轻量 SOAP 处理引擎
XFire
是新一代WebService框架,支持一系列Web Service的新标准--JSR181、WSDL2.0 、JAXB2、WS-Security等

三 用XFire进行Web Service开发
  3.1 XFire简介
       在架构上可以把XFire大概分为 Service, Transport 和 Invoker 三个层面。 Service 层是 XFire 架构的静态基础,负责完成对服务的注册及其管理。核心的 ServiceRegistry 接口完成对服务自身的生命期管理,如注册/注销/获取等等;而 ServiceFactory 接口则负责从具体的 POJO 类型,生成实现 Service接口的可被管理的服务代理。 Transport 层则是 XFire 的外部 IO 处理系统。由 TransportManager 接口对具体的 Transport 接口实现进行管理,默认提供了基于 pipe 的 LocalTransport 和基于 Http 协议的 SoapHttpTransport。理论上可以任意进行扩展,例如 XFire 发布包中还提供了基于 JMS 和 XMPP 的实现。 Invoker 则是 XFire 的动态调用层,负责在 Transport 层接受到请求后,解析内容、调用合适服务并最终返回 SOAP 封包给调用者。运行时 Invoker 负责维系 Service 和 Transport 之间的联系。
因此一个服务的生成和注册往往类似如下代码:
[code]Service endpoint = serviceFactory.create(clazz);// 根据具体类型创建服务
XFire.getServiceRegistry().register(endpoint);// 向服务管理注册服务 endpoint.setInvoker(new BeanInvoker(bean));// 设定服务调用模式 [/code]
最基本的XFire 接口,实际上就是 getServiceRegistry()和getTransportManager() 的封装。
XFire 中另一块核心的思想,就是其灵活而强大的 binding 机制,负责完成 Java 类型与 SOAP 消息的双向转换。XFire仅仅内建就支持 POJO(Aegis), Castor,JAXB 1.1, JAXB 2.0 和 XMLBeans 多种模式,你可以根据需求选择从全自动POJO生成,到全手工 xsd 文件定义的不同方式。

3.2 XFire目录介绍
XFire的官方下载地址: http://XFire.codehaus.org/
从官方网站下载到本地机器后解压,目录结构如下:
XFire-distribution-1.1-beta-1
|____api (javadoc文档)
|____examples (XFire例子)
|____lib (XFire所需的jars包)
|____modules (XFire模块)
|____XFire-all-1.1-beta-1.jar
|____几个授权和说明TXT文档

3.3 用XFire进行Web service开发步骤
A. Web service服务器端开发
(1)首先打开Eclipse,创建一个工程 .
(2)将需要的库文件copy到工程的lib目录下
(3) 创建一个简单的java文件
新建HelloService.java文件,该java文件只声明了一个简单的sayHello(String name)方法,文件内容如下:
package com.xiaomin.xfire;
/**

@author 蒲刚敏
*
* 说明:一个简单的web service服务器端程序
*/
public class HelloService {
/**

*说明: 传入一个名称,向其名称打招呼 
*
@param name 名称
*
@return 返回打招呼的内容
*/
public String sayHello(String name){
return name+",你好!";
}

注:检验Java类的方法和构造函数是公共的(public)

(4)在WebRoot/WEB-INF/classes目录下创建META-INF/XFire目录,然后在META-INF/XFire目录下创建services.xml文件。XFireConfigurableServlet会自动查找classes下的META-INF\xfire\services.xml配置文件,在这个XML配置文件中,把要提供服务类或接口进行绑定,设置其名称、命名空间、需要绑定的类service.xml文件的内容如下 :
         
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xfire.codehaus.org/config/1.0">
 
<service>
  
<name>HelloService</name>
  
<namespace>
   myWebService
  
</namespace>
  
<serviceClass>com.xiaomin.xfire.HelloService</serviceClass>
  
<!--
    <implementationClass>元素记录实现接口的Java类名。这是一个可选元素,如果前一个元素<serviceClass>

填入的是接口,那么此处就要填入相应的实现类名。
   <implementationClass> </implementationClass>
   
-->
<!– 身份验证
<inHandlers
>
     
<handler handlerClass="com.xiaomin.xfire.AuthenticationHandler"></handler>
  
</inHandlers> -->
 
</service>
 
<!-- 
 <service>
  <name>HelloTwoService</name>
  <namespace>
   http://localhost:8080/testService/HelloTwoService
  </namespace>
  <serviceClass>com.xiaomin.xife.HelloTwoService</serviceClass>
 </service>
 
-->
</beans>
这个文档定义了你要发布的Web服务,定义了一个名为HelloService的服务,服务类为com.xiaomin.xfire.HelloService。具体解释如下:
对Web服务的定义包含在元素内,元素下还有若干子元素。 第一个子元素是,你可以提供任何有效的xml名字,这个名字会被客户端程序和服务器上的其他组件使用。例如,当服务器起来以后,你可以在浏览器上使用这个名称来查看WSDL。 下一个子元素是,任何有效地xml名称都可以,将作为你服务器的唯一标识变量使用。 元素包含Java类名用来指明方法的签名。
元素记录实现接口的Java类名。这是一个可选元素,如果前一个元素填入的是接口,那么此处就要填入相应的实现类名。

(5)建立WEB.XML文件的配置映射,当访问services时,调用XFireConfigurableServlet来处理,在web.xml文件中加入如下内容:
<servlet>
  
<servlet-name>XFireServlet</servlet-name>
  
<servlet-class>
   org.codehaus.xfire.transport.http.XFireConfigurableServlet
  
</servlet-class>
  
<load-on-startup>0</load-on-startup>
  
</servlet>
  
<servlet-mapping>
  
<servlet-name>XFireServlet</servlet-name>
  
<url-pattern>/services/*</url-pattern>
  
</servlet-mapping>
(6)起动TOMCAT进行服务器端的测试
输入网址:http://localhost:8080/testService/services/

浏览器会显示你所绑定的类或接口名称,出现如下内容则服务器端成功:

Available Services:
HelloService [wsdl]
Generated by XFire ( http://xfire.codehaus.org )

点击服务名后面的[wsdl],会显示该服务类的详细wsdl描述。

B. Web Service 客户端开发
(1)创建个服务模型接口类(HelloServiceInf.java),内容如下:
package com.xiaomin.xfire.client;

public interface HelloServiceInf {
public String sayHello(String name);
}
(2)创建测试类(TestClient.java),内容如下:
import org.codehaus.xfire.XFireFactory;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import com.xiaomin.xfire.client.HelloServiceInf;
public class TestClient {
public static void main(String[] args){
  
final String  HELLO_URL = "http://localhost:8080/testService/services/HelloService";
  
//创建服务模型
  Service srvcModel = new ObjectServiceFactory().create(HelloServiceInf.class);
  
//获取XFire的代理对象
  XFireProxyFactory factory = new XFireProxyFactory(XFireFactory.newInstance().getXFire());
  
  
try {
  
//通过proxyFactory,使用服务模型和服务终点URL(用于获得WSDL),我们获得了服务的本地代理。这个代理就是实际的客户端。
  HelloServiceInf hell = (HelloServiceInf) factory.create(srvcModel, HELLO_URL);
  System.out.println(hell.sayHello(
"sky"));
  } 
catch (MalformedURLException e) {
   e.printStackTrace();
  }
}
}
在eclipse运行,如在控制台输出:sky,你好!  则表示客户端成功!

C. Web service身份验证
这里采用soap header方式
(1)身份验证目的
安全,服务只能被授权用户访问
(2)服务器端设置授权用户,首先我们编写服务端验证类继承AbstractHandler,代码如下:

package com.xiaomin.xfire;

import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.handler.AbstractHandler;
import org.jdom.Element;

/**
 * 
 * 
@author 蒲刚敏
 *
 * 说明:服务器端设置身份验证
 
*/
public class AuthenticationHandler extends AbstractHandler{
   
    
public void invoke(MessageContext cfx) throws Exception{
      
if(cfx.getInMessage().getHeader() == null)
           {
               
throw new org.codehaus.xfire.fault.XFireFault("请求必须包含验证信息",org.codehaus.xfire.fault.XFireFault.SENDER);
           }
           Element token
=cfx.getInMessage().getHeader().getChild("AuthenticationToken");
           
if (token == null
           { 
            
throw new org.codehaus.xfire.fault.XFireFault("请求必须包含身份验证信息", org.codehaus.xfire.fault.XFireFault.SENDER); 
           } 

              String username 
= token.getChild("Username").getValue(); 
              String password 
= token.getChild("Password").getValue(); 
              
try 
              { 
                  
//进行身份验证 ,只有abcd@1234的用户为授权用户
                 if(username.equals("abcd"&& password.equals("1234"))
                  
//这语句不显示
                  System.out.println("身份验证通过");
                 
else throw new Exception();
              } 
              
catch (Exception e) 
              { 
                  
throw new   org.codehaus.xfire.fault.XFireFault("非法的用户名和密码",   org.codehaus.xfire.fault.XFireFault.SENDER); 
              } 

          } 
}

(3)为要进行身份验证的服务绑定Handler,修改Services.xml文件内容:
   
<service>
  
<name>HelloService</name>
  
<namespace>
   myWebService
  
</namespace>
  
  
<serviceClass>com.xiaomin.xfire.HelloService</serviceClass>
  
<!--
    <implementationClass>元素记录实现接口的Java类名。这是一个可选元素,如果前一个元素<serviceClass>

填入的是接口,那么此处就要填入相应的实现类名。
   <implementationClass> </implementationClass>
   
-->
  
<!—此处为进行绑定的Handler-->
   
<inHandlers>
     
<handler handlerClass="com.xiaomin.xfire.AuthenticationHandler"></handler>
  
</inHandlers>

 
</service>
(4)编写客户端发送授权信息类ClientAuthenticationHandler继承AbstractHandler
代码:
 
package com.xiaomin.xfire.client;

import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.handler.AbstractHandler;
import org.jdom.Element;
/**
 * 
 * 
@author 蒲刚敏
 *
 * 说明:客户端发送授权信息
 
*/
public class ClientAuthenticationHandler extends AbstractHandler{

     
private String username = null

     
private String password = null

     
public ClientAuthenticationHandler(){ 

     } 

     
public ClientAuthenticationHandler(String username,String password){ 

         
this.username = username; 

         
this.password = password; 
     } 

     
public void setUsername(String username) { 

         
this.username = username; 

     } 

     
public void setPassword(String password){ 

         
this.password = password; 

     } 

     
public void invoke(MessageContext context) throws Exception { 

         
//为SOAP Header构造验证信息
         Element el = new Element("header"); 
         context.getOutMessage().setHeader(el); 
         Element auth 
= new Element("AuthenticationToken"); 
         Element username_el 
= new Element("Username"); 
         username_el.addContent(username); 
         Element password_el 
= new Element("Password"); 
         password_el.addContent(password); 
         auth.addContent(username_el); 
         auth.addContent(password_el); 
         el.addContent(auth); 
     } 
}
(5)在客户端获取了服务的本地代理之后加入Header,需加入的代码:
 
XFireProxy proxy = (XFireProxy)Proxy.getInvocationHandler(hell);
  Client client 
= proxy.getClient();
  client.addOutHandler(
new ClientAuthenticationHandler("abcd""1234"));
这样我们就完成了编码,下面启动tomcat,运行客户端代码,本文为abcd@1234位授权用户,使用abcd@1234,可以正常访问WS,如果用错误帐号,则会有以下异常
Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: 非法的用户名和密码
org.codehaus.xfire.fault.XFireFault: 非法的用户名和密码
    at org.codehaus.xfire.fault.Soap11FaultSerializer.readMessage(Soap11FaultSerializer.java:31)
    at org.codehaus.xfire.fault.SoapFaultSerializer.readMessage(SoapFaultSerializer.java:28)
    at org.codehaus.xfire.soap.handler.ReadHeadersHandler.checkForFault(ReadHeadersHandler.java:111)
    at org.codehaus.xfire.soap.handler.ReadHeadersHandler.invoke(ReadHeadersHandler.java:67)
    at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)
    at org.codehaus.xfire.client.Client.onReceive(Client.java:406)
    at org.codehaus.xfire.transport.http.HttpChannel.sendViaClient(HttpChannel.java:139)
    at org.codehaus.xfire.transport.http.HttpChannel.send(HttpChannel.java:48)
    at org.codehaus.xfire.handler.OutMessageSender.invoke(OutMessageSender.java:26)
    at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:79)
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114)
    at org.codehaus.xfire.client.Client.invoke(Client.java:336)
    at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77)
    at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57)
    at $Proxy0.getUser(Unknown Source)
    at test.ClientTest.main(ClientTest.java:39)