很久没写原创文章了。今天奉献给大家的是关于 Web Service 方面的文章。说起来惭愧,关于 Web Service,我从大二的时候就开始关注了,那时在做一套学生管理系统,可能是好奇,可能是图新鲜,可能是被跨平台所吸引,在还没弄得很清楚的情况下就迷恋上了这种技术,抱着李维的《Delphi 6/Kylix 2 SOAP/Web Service程序设计篇》狂啃,到了大三也没作出个像样的东西:(。大四上学期,签到 AUO 实习,并在 AUO 做毕设,毕设选题还是 Web Service 方面,论文洋洋洒洒写了一大篇,结果,到了最后的程序实现时,仅仅是以 XML 格式进行数据库和 Web 页面的交互而已。(再惭愧一次)
重新捡起 Web Service 是去年的事情,当时评估了多种开源 SSO 实现,总觉得不是很方便,遂打算自己实现。为了使通用性更高,决定让 Web Service 完成。并很自然的选到了 Axis。
SOAP、WSDL、UDDI,这些名词相信只要了解过 Web Service 的都不陌生,根据 Apache 的定义,Axis 是一种 W3C SOAP 实现,国内有些介绍还特别注明了:Axis 并不完全是 SOAP 引擎,它还包括独立的 SOAP 服务器、嵌入 Servlet 引擎的服务器、支持 WSDL 并提供转化 WSDL 为 Java 类的工具、例子程序、TCP/IP 数据包监视工具,等等。Axis 部署 Web Serive 有两种方式,最简单的是拷贝 java 源代码文件到 web 文件夹下把扩展名改为 .jws 直接调用,可参考这篇文章:
用Axis 1.1 for Java进行Web Services开发(1)。另一种方式是通过 WSDD(Web Services描述文档)部署,可参考:
使用Axis发布简单的Web服务。在我的应用中,使用的是后者,以便 Axis 进行自动序列化/反序列化处理。
实现一次 SSO 登陆验证,最少要传入用户名、密码。为了达到这种目的,在客户端我们构造 User 对象(本文中 User 对象仅包含用户名和密码),并通过 Axis 自动序列化传递出去;到了 SSO 端,Axis 自动反序列化之后还原成 User 对象;最后返回给客户端说明本次登陆的结果,返回的结果不仅仅包含例如“登陆成功”之类的简单信息,也许还有很多其他信息,看来创建一个叫做 Respond 的对象(本文中 Respond 对象仅包含登陆 ID 和结果描述)很有必要了,把 Respond 传回给客户端说明登陆结果。
暴露给客户端供登陆验证的服务类是 AuthService。该类代码简单表示如下:
public class AuthService {
/**
* 验证用户名和密码
* @param String userName 用户名
* @param String passWord 密码
* @return Respond 登陆验证后返回
*/
public Respond login(User user){
String name = user.getName();
String password = user.password();
//进行数据库验证
//..
//
Respond respond = new Respond();
respond.setId("123");
respond.setDesc("登陆成功");
return respond;
}
} User 和 Respond 以及服务类都写好了。通过命令行方式,我生成了 server-config.wsdd,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<globalConfiguration>
<parameter name="sendMultiRefs" value="true"/>
<parameter name="disablePrettyXML" value="true"/>
<parameter name="adminPassword" value="admin"/>
<parameter name="attachments.Directory" value="D:\workspace\SSO\web\WEB-INF\attachments"/>
<parameter name="dotNetSoapEncFix" value="true"/>
<parameter name="enableNamespacePrefixOptimization" value="true"/>
<parameter name="sendXMLDeclaration" value="true"/>
<parameter name="sendXsiTypes" value="true"/>
<parameter name="attachments.implementation" value="org.apache.axis.attachments.AttachmentsImpl"/>
<requestFlow>
<handler type="java:org.apache.axis.handlers.JWSHandler">
<parameter name="scope" value="session"/>
</handler>
<handler type="java:org.apache.axis.handlers.JWSHandler">
<parameter name="scope" value="request"/>
<parameter name="extension" value=".jwr"/>
</handler>
</requestFlow>
</globalConfiguration>
<handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/>
<handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
<handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
<service name="AuthService" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="com.cdmcs.sso.AuthService"/>
<beanMapping languageSpecificType="java:sso.Respond" qname="ns:resp" xmlns:ns="urn:BeanService"/>
<beanMapping languageSpecificType="java:sso.User" qname="ns:user" xmlns:ns="urn:BeanService"/>
</service>
<service name="AdminService" provider="java:MSG">
<parameter name="allowedMethods" value="AdminService"/>
<parameter name="enableRemoteAdmin" value="false"/>
<parameter name="className" value="org.apache.axis.utils.Admin"/>
<namespace>http://xml.apache.org/axis/wsdd/</namespace>
</service>
<service name="Version" provider="java:RPC">
<parameter name="allowedMethods" value="getVersion"/>
<parameter name="className" value="org.apache.axis.Version"/>
</service>
<transport name="http">
<requestFlow>
<handler type="URLMapper"/>
<handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>
</requestFlow>
<parameter name="qs:list" value="org.apache.axis.transport.http.QSListHandler"/>
<parameter name="qs:wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
<parameter name="qs.list" value="org.apache.axis.transport.http.QSListHandler"/>
<parameter name="qs.method" value="org.apache.axis.transport.http.QSMethodHandler"/>
<parameter name="qs:method" value="org.apache.axis.transport.http.QSMethodHandler"/>
<parameter name="qs.wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
</transport>
<transport name="local">
<responseFlow>
<handler type="LocalResponder"/>
</responseFlow>
</transport>
</deployment>
要说明的是,深究上述配置文件具体含义不是本文的目的,要对其具体了解,请参考 Axis 文档。其中,只有下面的 XML 才是我们感兴趣的:
<service name="AuthService" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="com.cdmcs.sso.AuthService"/>
<beanMapping languageSpecificType="java:sso.Respond" qname="ns:resp" xmlns:ns="urn:BeanService"/>
<beanMapping languageSpecificType="java:sso.bo.User" qname="ns:user" xmlns:ns="urn:BeanService"/>
</service>
为了完成自动序列化/反序列化,我们使用“beanMapping”元素指定要进行处理的 bean 文件。只有在 WSDD 中定义了这些,才能享受到 Axis 带来的自动序列化/反序列化优势。
客户端代码:
public class TestClient {
public static void main(String[] args) {
try {
String endpoint = "http://127.0.0.1:8080/services/AuthService?wsdl";
Service service = new Service();
Call call = (Call) service.createCall();
QName qn = new QName("urn:BeanService","resp");
QName qx = new QName("urn:BeanService","user");
//注册 bean
call.registerTypeMapping(Respond.class,qn,new BeanSerializerFactory(Respond.class, qn),new BeanDeserializerFactory(Respond.class, qn));
call.registerTypeMapping(User.class,qx,new BeanSerializerFactory(User.class, qx),new BeanDeserializerFactory(User.class, qx));
call.setTargetEndpointAddress(new java.net.URL(endpoint));
call.setOperationName(new QName("http://soapinterop.org/","login"));
User user = new User();
mul.setName("test");
mul.setPassword("test");
Respond respond = (Reopond) call.invoke(new Object[] {user});
System.out.println("登陆,返回'" + respond.getDesc() + "'。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
正如我们期望的,打印出“登陆成功”。通过上面的范例,我们发现,Axis 的自动序列化/反序列化机制还是很方便的,除了 bean 以外,其他类型的对象也可以让 Axis 来完成,具体参考 Axis 文档,如果要传递的对象 Axis 未提供自动序列化/反序列化支持,请考虑人工实现,参考:
深度编程Axis序列化/反序列化器开发指南。
请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处:http://www.blogjava.net/rosen