加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

axis+webservice教程

发布时间:2020-12-16 23:35:36 所属栏目:安全 来源:网络整理
导读:利用 AXIS 开发 Webservice(一) —— 如何 发布自己的 webservice 先介绍下本人开发环境吧。 JDK 1.4.2 + Myeclipse 6.0(实在经不起诱惑,尝尝鲜) + Tomcat 5.0.28 + AXIS 1.4。 AXIS 1.4 包可以在 http://ws.apache.org/axis/找到。 假设所有的环境你已

利用 AXIS 开发 Webservice(一) —— 如何

发布自己的 webservice

先介绍下本人开发环境吧。 JDK 1.4.2 + Myeclipse 6.0(实在经不起诱惑,尝尝鲜) + Tomcat

5.0.28 + AXIS 1.4。 AXIS 1.4 包可以在 http://ws.apache.org/axis/找到。 假设所有的环境你已

经搭好,并且 AXIS 包也已经下好了。OK, Here we go~

????? 解压 axis-bin-1_4.zip 这个包可以看到 webapps 目录,双击进入把里面的 AXIS 文件夹拷

到 %TOMCAT_HOME%webapps 目 录 下 , 之 后 拷 贝 activation.jar 、 mail.jar 、 tools.jar

到 %TOMCAT_HOME%webappsaxisWEB-INFlib 目 录 下 。 启 动 tomcat , 访 问

http://localhost:8080/axis/happyaxis.jsp 如果访问成功,恭喜你!基本的配置你已经做完了。

PS:此处的%TOMCAT_HOME%指的是 Tomcat 的安装目录,至于那另外的三个 jar 包,J2EE

1.4 库里就能找的到。

现在来说一下最关键的 Webservice 的发布。AXIS 提供了两种发布方式,一种是即

时发布(Instant Deployment),另外一种是定制发布(Custom Deployment)。即时发布提供

了一种非常简单的 webservice 的发布方式,但是其中限制太多,因此在实际的开发中定制发

布才是首选。这里也将会以定制发布为重点来介绍。

1.即时发布 JWS (Java Web Service) Files - Instant

Deployment

?即时发布提供了一种非常简单发布方式,发布者只要有 Java 源代码(也就是.java 文件),

然 后 把 其 后 缀 名 改 成 jws ( 也 就 是 java web service 的 缩 写 ) 拷 贝

到%TOMCAT_HOME%webappsaxis 目录下即完成了所有的发布工作。AXIS 的编译引擎会

处理接下来的所有事情。下面是一段示例代码:

java 代码

public class HelloAXIS {

public String Hello(String name){

???? return "AXIS say hello to " + name;

}

}

??? 把HelloAXIS.java 文 件 改 成HelloAXIS.jws 然 后 拷 贝

到 %TOMCAT_HOME%webappsaxis 目 录 下 , 启 动 Tomcat 之 后 访 问


http://localhost:8080/axis/HelloAXIS.jws 如果能看到 Click to see the WSDL 这个超链接就说

明已经发布成功了,点击进去就可以看到这个 Webservice 的 WSDL 描述文件。server 端的

发布已经完成了,接下来就是编写 Client 端测试代码了。

java 代码

package com.chnic.test;

import java.net.URL;

import javax.xml.namespace.QName;

import org.apache.axis.client.Call;

import org.apache.axis.client.Service;

public class Test {

public static void main(String[] args) throws Exception{

???? String targetEendPoint = "http://localhost:8080/axis/HelloAXIS.jws";

???? Service service = new Service();

???? Call call = (Call) service.createCall();

???? call.setOperationName(new QName(targetEendPoint,"Hello"));

???? call.setTargetEndpointAddress(new URL(targetEendPoint));

???? String result = (String) call.invoke(new Object[]{"Robert"});

???? System.out.println(result);

}

}

测试代码很简单,如果熟悉 java 反射机制的朋友不用两分钟就能看明白。运行后客

户端控制台出现 AXIS say hello Robert。测试成功。果然很简单吧。不过在这简单背后却是

以牺牲灵活性为代价的。假如你现在手里只有.class 或者一个 jar 包,jws 就不再能满足你的

需求了,最要命的就是即时发布不支持带包的类,这点 AXIS 的用户手册上写的也很明白。

?Important: JWS web services are intended for simple web services. You cannot use packages in

the pages,and as the code is compiled at run time you can not find out about errors until after

deployment. Production quality web services should use Java classes with custom deployment.

2.定制发布 Custom Deployment - Introducing WSDD

????? 比起即时发布定制发布更加烦琐也更复杂,但是换来的却是更大的灵活性,因此在实

际项目中定制发布还是不二的选择。定制发布需要你自己编写一个 WSDD(Web Service

Deployment Descriptor)文件,这个东东类似与 XML 稍后会做出介绍。废话不多说,我们


来看代码:

java 代码

package com.chnic.webservice;

public class HelloWorld {

public HelloWorld(){

}

public String hello(String str){

???? return "Hello " + str;

}

public int add(int a,int b){

???? return a + b;

}

}

??? 一 个 带 包 的 很 简 单 的 类 , 在 eclipse 下 编 译 后 按 照 包 名 拷

到 %TOMCAT_HOME%webappsaxisWEB-INFclasses 目录下。以这个类为例,拷贝完之

后这个HelloWorld.class的路径就

是 %TOMCAT_HOME%webappsaxisWEB-INFclassescomchnicwebservice。PS: 如果嫌

这样太麻烦,可以另外建一个 Java Web 工程用 myeclipse 的发布工具发布到 Tomcat 之后,

整体一次性拷贝到 websericve 的工程中。

?? 接下来就需要编写发布文件deploy.wsdd 。

到%TOMCAT_HOME%webappsaxisWEB-INF 目录下建立这个文件并在其中添加如下内

容:

xml 代码

<deployment xmlns=http://xml.apache.org/axis/wsdd/

???????????? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

??? <service name="HelloWorld" provider="java:RPC">

???????? <parameter name="className" value="com.chnic.webservice.HelloWorld"/>

???????? <parameter name="allowedMethods" value="*"/>

??? </service>

</deployment>


???? 简单的介绍下各个节点的含义,"HelloWorld"当然是这个 webservice 的名字,后面紧

跟的 java:RPC 指的是服务类型。这里一共有有 4 种类型,分别是:RPC,Document,Wrapped

和 Message。有兴趣可以看下 org.apache.axis.providers 这个包和子包下面的类的 API 文档。

之后的 parameter 节点第一个当然是指出具体的类,第二个从字面上也很好理解:允许调用

的方法。这里的配置告诉引擎可以调用所有的 public 方法,当然你也可以自己指定。

? 编 写 完 配 置 发 布 文 件 之 后 , cmd 打 开 windows 的 控 制 台 , 进

入%TOMCAT_HOME%webappsaxisWEB-INF 目录下键入如下命令

java -Djava.ext.dirs=lib org.apache.axis.client.AdminClient deploy.wsdd

自己做用下面的批处理成功了
set Axis_Lib=D:tomcat6.0.29webappsWSTestWEB-INFlib
set Java_Cmd=java -Djava.ext.dirs=%Axis_Lib%
set Axis_Servlet=http://localhost:8080/WSTest/services/AxisServlet
%Java_Cmd% org.apache.axis.client.AdminClient -l%Axis_Servlet% depl.wsdd

????????? 之后控制台返回 Processing file deploy.wsdd 和 Done processing 这两段话即说明发

布成功。此时会在同级目录生成一个 server-config.wsdd 文件)(在这里的 AdminClient 是 AXIS

提供的一个客户端管理工具。至于 java.ext.dirs 的含义可以去了解一下 classloader 和 JVM 类

装载机制方面的知识,在这里就不多解释。 还有一点要注意的是在发布的时候 Tomcat 服务

必须处于启动状态,否则就会抛出一堆无法连接的异常信息。发布成功之后你可以通过访问

http://localhost:8080/axis/servlet/AxisServlet 来查看你所有的定制发布的服务。

客户端测试代码

String targetEendPoint = "http://localhost:8080/axis/services/HelloWorld";

Service service = new Service();

Call call = (Call) service.createCall();

call.setTargetEndpointAddress(new URL(targetEendPoint));

call.setOperationName(new QName(targetEendPoint,"hello"));

String result = (String) call.invoke(new Object[]{"Robert"});

System.out.println(result);

call.setOperationName(new QName(targetEendPoint,"add"));

Integer res = (Integer) call.invoke(new Object[]{new Integer(1),new Integer(2)});

System.out.println("The result is: " + res);

运行测试代码,控制台显示 Hello Robert 和 The result is: 3 这两句话,说明发布成

功。仔细观察下发现其实除了那个 targetEndpoint 之外,即时发布和定制发布的客户端调用

代码基本上都是一样的。定制发布的 URL 可以在 WSDL 文件里找到。 其实定制发布还有

一些高级特性,这个就留到下一篇再说吧。


利 用 AXIS 开 发 Webservice( 二 ) — —

WSDD 的一些高级特性

1、取消发布一个 webservice

开篇之前先把上篇的一个遗漏补充上,上篇只讲了怎么发布一个 webservice,但是如何取消

没有却没有提。其实取消一个已经发布的 webservce 也是非常简单的,我们就拿上篇的

HelloWorld 来做例子吧。

发布 webservice 的时候我们有一个 deploy.wsdd 文件,当然在取消发布的时候就会有一个

undeploy.wsdd 文件。这个文件的内容也很简单,xml 的代码如下。

Xml 代码

<undeployment xmlns="http://xml.apache.org/axis/wsdd/">

?<service name="HelloWorld"/>

</undeployment>

<undeployment xmlns="http://xml.apache.org/axis/wsdd/">

?<service name="HelloWorld"/>

</undeployment>

编写完这个 xml 文件之后,把它同样 copy 到%TOMCAT_HOMwebappsaxisWEB-INF 目录

下,然后 CMD 打开控制台,在控制台输入一个我们很熟悉的命令

java -Djava.ext.dirs=lib org.apache.axis.client.AdminClient undeploy.wsdd

运行之后得到如下结果说明取消发布成功

Processing file undeploy.wsdd

<Admin>Done processing</Admin>

2、WSDD 的高级特性

说完取消发布之后就来说一下 AXIS 的一些高级特性,AXIS 在编写 deploy.wsdd 这个文件时,

每个<service>节点下面会有这样一个子节点。


Xml 代码

<parameter name="scope" value="value"/>

<parameter name="scope" value="value"/> 这 个 节 点 配 置 着 你 的 service object 也 就 是 你

webservice 服务的那个 object,在后面的 value 里可以有三个选项 request,session,or

application。熟悉 Jsp、Servlet、或者 EJB 里的 SessionBean 的朋友应该能很快能明白这个三

个配置选项的含义。

requst:这个选项会让 AXIS 为每一个 SOAP 的请求产生一个服务对象,可以想像如果这个

webservice 的对象足够复杂,而且 SOAP 的请求过多,这个选项是非常耗费服务器性能的。

session:如果选择了 session,程序就会给每个调用这个 webservice 的客户端创造一个服务对

象。

application:这个选项最彪悍,程序只会在内存里 new 出来一个服务对象,然后为所有

webservice 客户端服务。很显然这个选项不能储存客户端的一些个性化数据。所以在功能性

上很多时候不能满足要求。

3、高级特性 Handler

接下来说一下 Axis 的 Handler 和 Chain 机制,Handler 和 Chain 是 Axis 引擎提供的一个很强

大的工具。假如现在客户有这样一个需求,需要记录某一个 webservice 被调用的次数,这个

时候如果在 service object 里去实现这个功能不仅麻烦,而且侵入了原有的程序也会对增加原

有程序的不稳定性。有了 Handler 我们就能见 easy 的解决这个问题。我们先来编写 handler

的代码。

Java 代码

package com.chnic.handler;

import org.apache.axis.AxisFault;

import org.apache.axis.MessageContext;

import org.apache.axis.handlers.BasicHandler;

public class HelloWorldHandler extends BasicHandler{

private static final long serialVersionUID = 1L;

public void invoke(MessageContext context) throws AxisFault {

???? String status = (String) this.getOption("status");

???? System.out.println("HelloWorldHandler's status is: " + status);

}

}

package com.chnic.handler;


import org.apache.axis.AxisFault;

import org.apache.axis.MessageContext;

import org.apache.axis.handlers.BasicHandler;

public class HelloWorldHandler extends BasicHandler{

private static final long serialVersionUID = 1L;

public void invoke(MessageContext context) throws AxisFault {

???? String status = (String) this.getOption("status");

???? System.out.println("HelloWorldHandler's status is: " + status);

}

}

BasicHandler 是一个抽象类,Axis 提供了很多 Handler 的具体实现,BasicHandler 只是其中

最简单的一个。要实现一个自己的 handler 首先要从继承 BasicHandler 这个类开始并实现其

中的 invoke(MessageContext arg)这个方法。MessageContext 可以看成是一个 Axis 的上下文,

里面存储的是一些 Axis 和 webservice 的基本信息。想了解的朋友可以看一下 Axis 的 API。

编写完 Handler 代码之后我们连编写发布文件。

Xml 代码

<deployment xmlns="http://xml.apache.org/axis/wsdd/"

???? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

??? <handler name="Hello" type="java:com.chnic.handler.HelloWorldHandler">

??????? <parameter name="status" value="success"/>

??? </handler>

??? <service name="HelloWorld" provider="java:RPC">

????? <requestFlow>

??????? <handler type="Hello"/>

????? </requestFlow>

????????? <parameter name="className" value="com.chnic.webservice.HelloWorld"/>

????????? <parameter name="allowedMethods" value="*"/>

????????? <parameter name="scope" value="session"/>

??? </service>

</deployment>

发布代码中有这样的一句,细心的朋友一定会发现。

Xml 代码

<parameter name="status" value=" session "/>

<parameter name="status" value=" session "/>看完这句代码再对比一下 Handler 的实现代码中

的一句,相信大多数人都能明白了。

Java 代码

String status = (String) this.getOption("status");


String status = (String) this.getOption("status");Handler 通过 getOption(String)这个方法拿到了

配置文件中我配置的属性值。而我们上述所做的所有工作对于原来的 Webserivce 来说都是

透明的,不会对侵入原有的程序当中。 一个 Handler 可以被多个 service 所使用,通过

<requestFlow> 这 个 标 签 来 引 用 到 某 一 个 service 中 , 这 里 还 要 多 提 一 句 既 然 是 一 个

requestFlow,这个当然可以加不只一个的 Handler。接下来编写测试代码运行。在本地应用

服务器上会打出如下语句:HelloWorldHandler's status is: session 说明测试成功,而且 handler

是配置在 requestflow 标签中所以这段代码会在 service 代码之前先执行。

4、高级特性 Chain

介绍完了 Handler 再来介绍 Chain。从 Chain 的字面意思就能猜到他实现的一连串 Handler

的功能。假如某个 service 需要不止一个 Handler,或者要根据 Client 的情况来选择需要那些

Handler。特别是后一个需求,我们无法用一个或者几个 Handler 来解决,这个时候我们就需

要<Chain>来实现了。我们先再编写一个 Handler,加上之前的那个 Handler 我们来组成一条

锁链。

Java 代码

package com.chnic.handler;

import org.apache.axis.AxisFault;

import org.apache.axis.MessageContext;

import org.apache.axis.handlers.BasicHandler;

public class MyHandler extends BasicHandler {

private static final long serialVersionUID = 1L;

public void invoke(MessageContext context) throws AxisFault {

???? System.out.println("This is MyHandler..");

}

}

之后我们编写 Chain 的代码

Java 代码

package com.chnic.chain;

import org.apache.axis.SimpleChain;

import com.chnic.handler.HelloWorldHandler;

import com.chnic.handler.MyHandler;

public class HelloWorldChain extends SimpleChain {

private static final long serialVersionUID = 1L;

public HelloWorldChain(){


HelloWorldHandler hwh = new HelloWorldHandler();

MyHandler mh = new MyHandler();

this.addHandler(hwh);

this.addHandler(mh);

}

}

在 Chain 的构造函数中,把我要的两个 Handler 用 addHandler()方法加载进去。之后我们来

编写发布文件。<chain>和<handler>元素有些许不同在这里有必要多句嘴。

<chain>元素中的子元素只允许是<handler>或者<chain>。后者也就是允许在“锁链”里再嵌

套“锁链”,在这里就拿嵌套<handler>来举例,他同样有两种方式来实现。第一种是直接包

含<handler>:

Xml 代码

<chain name="myChain">

??? <handler type="java:com.chnic.handler.MyHandler"/>

</chain>

<chain name="myChain">

??? <handler type="java:com.chnic.handler.MyHandler"/>

</chain>

第二种是引用别的<handler>

Xml 代码

<handler name="myHandler" type="java:com.chnic.handler.MyHandler"/>

<chain name="myChain"/>

?? <handler type="myHandler"/>

</chain>

因为我们这里的 Handler 并没有由 BasicHandler 来实现,而是由继承 SimpleChain 这个类来

实现,严格意义上讲,SimpleChain 也可以算是一个 Handler,因为 SimpleChain 也是从

BasicHandler 继承而来,他同样实现了 invoke()这个方法。下面回归正题,来看我们的发布

代码。

Xml 代码

<deployment xmlns="http://xml.apache.org/axis/wsdd/"

??? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<chain name="myChain">

??? <handler type="java:com.chnic.chain.HelloWorldChain"/>

</chain>


<service name="HelloWorld" provider="java:RPC">

?<requestFlow>

?? <chain type="myChain"/>

?</requestFlow>

???????? <parameter name="className" value="com.chnic.webservice.HelloWorld"/>

???????? <parameter name="allowedMethods" value="*"/>

???????? <parameter name="scope" value="session"/>

??? </service>

</deployment>

从新发布 webservice 之后,运行我们的测试代码。会发现在应用服务器的本地控制台上打出

两句 Handler 要输出的语句,说明测试成功。而且控制台打出语句的顺序是和我们加载

handler 的顺序一样的。

除了<requestFlow>之外,Axis 还提供了与之相应的</responseFlow>,用法和是 requestflow

一样的,所不同的是一个在 service 执行之前一个是之后。可以加下面代码到发布文件中的

<service>元素下就可以实现 responseflow 的功能

Xml 代码

<responseFlow>

???? <handler type="HelloWorldHandler"/>

?</responseFlow>

最后简单提一下远程管理,也就是 Remote Administration。要实现远程管理在发布文件的

<service>标签下加入下面一段语句就可以了。

Xml 代码

<parameter name="enableRemoteAdmin" value="true"/>

不过远程管理会有安全方面的问题,所以不建议使用。

一些高级部分就先说到这里,还有一些比如 RPC docuement Wrapped Message 的区别和使,

如何传递一个 Javabean,还有一些配置方面的的问题就留到下节说吧。


利用 AXIS 开发 Webservice(三) —— 如何

传递 javabean 和你的对象

在第一篇介绍 Axis 的文章里,我们做了一个简单的 webservice,我们 client side 传递了 String

和 int 类型的数据给 service object。Service 处理之后返回处理结果给 Client。对于大多数需

求,那个 demo 显然已经足够应付了。但是如果 client 端需要传输一个对象给 server,那么

那个 demo 就显得力不从心了。Axis 中提供了远程传输对象的方法,通过那些方法我们同样

可以随心的传递自己的对象。

我们先从传递一个 JavaBean 开始,首先编写一个 JavaBean。

Java 代码

package com.chnic.bean;

public class OrderBean {

???? private String name;

???? private int age;

? ???private String[] items;

???? private int[] price;

???? private boolean checked;

public OrderBean() {

}

public int getAge() {

???? return age;

}

public void setAge(int age) {

???? this.age = age;

}

public boolean isChecked() {

???? return checked;

}

public void setChecked(boolean checked) {

???? this.checked = checked;

}

public String[] getItems() {

???? return items;

}

public void setItems(String[] items) {


this.items = items;

}

public String getName() {

???? return name;

}

public void setName(String name) {

???? this.name = name;

}

public int[] getPrice() {

???? return price;

}

public void setPrice(int[] price) {

???? this.price = price;

}

}

这个 JavaBean 的前 4 个属性都很清楚,要解释一下最后一个。因为这个 JavaBean 被传递到

Service 端处理之后要接着被传回,用来示例 webservce 传进和传出。这个变量用来区别传进

和传出的差别。解释完我们来看下 service

Java 代码

package com.chnic.webservice;

import com.chnic.bean.OrderBean;

public class AnalyzeOrder {

public AnalyzeOrder(){

}

public OrderBean process(OrderBean order){

???? order.setChecked(true);

???? System.out.println("name: " + order.getName() + " age: " + order.getAge());

???? for(int i=0; i<order.getItems().length; i++)

????????? System.out.println("Item " + (i+1) + order.getItems()[i] +

???????????????????? " and price is " + order.getPrice()[i]);

???? return order;

}

}

Service Object 的代码很简单,就不解释了。惯例,我们来编写发布文件。

Xml 代码

<deployment xmlns="http://xml.apache.org/axis/wsdd/"

???? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">


??? <service name="CheckOrder" provider="java:RPC">

???????? <parameter name="className" value="com.chnic.webservice.AnalyzeOrder"/>

???????? <parameter name="allowedMethods" value="process"/>

???????? <beanMapping qname="myNS:Order" xmlns:myNS="urn:AnalyzeOrder"

????????????? languageSpecificType="java:com.chnic.bean.OrderBean"/>

??? </service>

</deployment>

唯一不同的就是多了<beabMapping>这个节点。qname 表示 XML 规范中定义的限定名称,

他由名称空间 URI、本地部分和前缀组成。除了本地部分其他都不是必须的,另外 QName

是不可变的。xmlns 后面的 myNS 是必须的。具体根据前面所指定的 qname 来决定。之后可

以随意命名一个 namespace。最后的 languageSpecificType 指定的是你传递的对象类型。第一

个属性的本地部分和第二个节点你自定义的命名空间会组成一个新的 QName,并将你要传

输的对象 mapping 是上去。

发布这个 webservice,编写测试代码

Java 代码

String targetEendPoint = "http://localhost:8080/axis/services/CheckOrder";

OrderBean order = new OrderBean();

order.setName("Beckham");

order.setAge(32);

String [] items = new String[] { "Ipod","ThinkPad" };

int [] price = new int [] { 999,5000 };

order.setItems(items);

order.setPrice(price);

order.setChecked(false);

System.out.println(order.isChecked());

Service service = new Service();

Call call = (Call) service.createCall();

QName qn = new QName("urn:AnalyzeOrder","Order" );

call.registerTypeMapping(OrderBean.class,qn,

??? new org.apache.axis.encoding.ser.BeanSerializerFactory(OrderBean.class,qn),

??? new org.apache.axis.encoding.ser.BeanDeserializerFactory(OrderBean.class,qn));

call.setTargetEndpointAddress( new java.net.URL(targetEendPoint) );

call.setOperationName( new QName("CheckOrder","process") );

call.addParameter( "arg1",ParameterMode.IN );

call.setReturnClass(OrderBean.class);


OrderBean result = (OrderBean) call.invoke( new Object[] {order} );

System.out.println("Success...");

System.out.println(result.isChecked())

从控制台输出一些信息以便测试,里面大多代码都应该见过。new QName 和之后的

registerTypeMapping 方法也是在实现我上面那段黑体字的内容。之后注册完参数和返回值类

型运行。

会发现在 tomcat 控制台和本地控制台都会输出测试结果,值得注意的是 checked 这个属性在

传入之前是 false 属性,传入之后我们改变了他的属性变为 true。返回之后在本地控制台打

印出来也为 true。

事实上 Axis 除了可以传输 JavaBean 之外还可以传输一些自定义的类型。比如 List、Map 和

时间日期类型。Axis 也为他们提供了专门的 SerializerFactory 和 DeserializerFactory。这些工

厂 类 会 产 生 串 行 化 工 具 去 序 列 化 相 应 的 对 象 。 更 多 的 Factory Object 可 以 在

org.apache.axis.encoding.ser 下面找到。

对于自定义序列化,Axis 也提供了相对性的配置节点。具体配置如下:

Xml 代码

<typeMapping qname="ns:local" xmlns:ns="someNamespace"

????????????? languageSpecificType="java:my.java.thingy"

????????????? serializer="my.java.Serializer"

????????????? deserializer="my.java.DeserializerFactory"

??????????? encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

typeMapping 的前三个属性和上面讲的 beanMapping 都一样。不一样的是后三个。serializer

指定的是串行化类,dserializer 指定的是反串行化类。最后一个指定的编码方式。其实对于

typeMapping 来说 beanMapping 只不过是他的一个简化版而已。因为 beanMapping 的串行化

和反串行化工厂类都是固定的,而编码方式也是固定的。

因为 typemapping 和 beanMapping 很相似,在这里就不再写 demo 了。 有些人也许会问,既

然能串行化对象那能不能“串行化”文件呢?


利用 AXIS 开发 Webservice(四) —— 如何

抛出自定义异常

上一篇介绍了如果在 Server 和 Client 端传递一个自己的对象。有些人也许会问传递异常行

不行?答案是可以。只不过传递异常的配置要稍微复杂一些。空口无凭,我还是用点代码来

说明。今天的例子稍微复杂点,用一下数据库(MySQL)。首先创建表和输入测试数据。

Sql 代码

create table users(id integer primary key,name varchar(20) not null);

insert into users values(1,'Lincoln'),(2,'Michael'),(4,'Mahone'),(6,'Sara');

一个 user 表, 条记录。4等会我们 client 段会发送一个 SOAP request 给 server 段,之后 server

段返回客户要的数据,如果没有则抛出一个自定义异常。表建立完成之后来编写 JavaBean。

Java 代码

package com.chnic.bean;

public class UserBean implements java.io.Serializable{

private static final long serialVersionUID = 1L;

private int id;

private String name;

public UserBean(){

}

public int getId() {

???? return id;

}

public void setId(int id) {

???? this.id = id;

}

public String getName() {

???? return name;

}

public void setName(String name) {

???? this.name = name;

}

}

Bean 有两个属性,Id 和 Name。client 会根据 ID 来取要的 Name。编写完 Bean 之后我们来


编写 Customer Exception 的代码。

Java 代码

package com.chnic.exception;

import java.rmi.RemoteException;

public class NoSuchUserException extends RemoteException {

private String errorMessage = "No such user: ";

private int id;

private static final long serialVersionUID = 1L;

public NoSuchUserException() {

}

public void printErrorMessage(){

???? System.out.println(errorMessage + id);

}

public int getId() {

???? return id;

}

public void setId(int id) {

???? this.id = id;

}

}

NoSuchUserException 这个类会记录在数据库没有相应数据的 ID 的值,然后返回给 Client。

值得注意的是,因为这个是个远程异常。所以要继承 RemoteException 这个类。两个要 transfer

的 Bean 完成之后。我们来编写 Service Ojbect 的代码。

Java 代码

package com.chnic.webservice;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import com.chnic.bean.UserBean;

import com.chnic.exception.NoSuchUserException;

import java.sql.DriverManager;


public class CheckUserInfo {

???? private String url = "jdbc:mysql://localhost:3306/test";

???? private String user = "root";

???? private String password = "root";

public CheckUserInfo(){

}

public Connection getConn(){

???? try {

?????????? Class.forName("com.mysql.jdbc.Driver");

?????????? return DriverManager.getConnection(url,user,password);

???? } catch (ClassNotFoundException e) {

?????????? e.printStackTrace();

???? } catch (SQLException e) {

?????????? e.printStackTrace();

???? }

???? return null;

}

public UserBean checkUser(int id) throws NoSuchUserException{

???? Connection conn = null;

???? try {

?????????? conn = this.getConn();

?????????? PreparedStatement statement =

??????????????? conn.prepareStatement("select * from users where id = ?");

?????????? statement.setInt(1,id);

?????????? ResultSet rs = statement.executeQuery();

?????????? boolean flag = false;

?????????? UserBean user = null;

while(rs.next()){

????? flag = true;

????? user = new UserBean();

????? user.setId(id);

????? user.setName(rs.getString(2));

}

rs.close();

if(flag)

????? return user;

else{

????? NoSuchUserException userException = new NoSuchUserException();


userException.setId(id);

throw userException;

???? }

} catch (SQLException e) {

???? e.printStackTrace();

}finally{

???? this.closeConn(conn);

}

return null;

}

public void closeConn(Connection conn){

???? try {

?????????? conn.close();

???? } catch (SQLException e) {

?????????? e.printStackTrace();

???? }

}

}

因为是 Demo 代码,代码写的比较粗糙,反正就是为了个演示。大家能看出来效果就好了。

代码很简单,接收到一个 id,然后在数据库里做匹配。如果找到匹配的了返回那个 userbean,

如果没找到就 throw 一个 Exception 出去。在这里多嘴一句。传递的 Bean 赋值的时候一定要

用 setXXX 方法,不能用构造函数传递,否则传递过去之后属性值会丢失。 你编写的那个

Bean 一定要严格遵循 JavaBean 规范。

之后我们来看 WSDD 发布文件。比起之前我们看到的 WSDD 文件,这次的稍微有点点复杂。

Xml 代码

<deployment xmlns="http://xml.apache.org/axis/wsdd/"

??? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<service name="AxisExceptionTest" provider="java:RPC">

???? <namespace>http://faults.samples</namespace>

???? <parameter name="className" value="com.chnic.webservice.CheckUserInfo"/>

???? <parameter name="allowedMethods" value="checkUser"/>

???? <parameter name="scope" value="Session"/>

<operation name="checkUser"

???????????? qname="operNS:checkUser"

???????????? xmlns:operNS="getSingleUser"

???????????? returnQName="getUserReturn"


returnType="rtns:User"

xmlns:rtns="http://faults.samples" >

??? <parameter name="id" type="tns:int"

????????? xmlns:tns="http://www.w3.org/2001/XMLSchema"/>

??? <fault name="NoSuchEmployeeFault"

???????????? qname="fns:fault"

???????????? xmlns:fns="http://faults.samples"

???????????? class="samples.faults.NoSuchEmployeeFault"

???????????? type="tns:NoSuchUserFault"

???????????? xmlns:tns="http://faults.samples"/>

</operation>

<typeMapping qname="myns:NoSuchUserFault"

??? xmlns:myns="urn:CustomerFault"

??? type="java:com.chnic.exception.NoSuchUserException"

??? serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

??? deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

??? encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

???????? <typeMapping qname="myns:User"

????????????? xmlns:myns="urn:CustomerBean"

????????????? type="java:com.chnic.bean.UserBean"

????????????? serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

????????????? deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

????????????? encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

??? </service>

</deployment>

首先不同的是多了个命名空间也就是 namespace 节点,等会测试代码中会看到用途。除了

namespace 之外还有 operation 这个节点和里面的 parameter 和 fault 子节点。先来介绍 operation

这个节点的属性。

name:操作名称或者方法名称,这个值会和你 server 发布的相关方法名匹配,所以要和方

法名相同。

qname:针对这个 operation 的限定名。

xmlns:针对这个 qname 的命名空间也就是 namespace。(不明白的可以看上一篇博文)

returnQName:这个元素节点所对应的方法返回出来对象的 Qname。


returnType:返回类型,注意和下面的 typemapping 的 qname 比较。

parameter 节点是这个 operation 指代的方法的参数,fault 节点代表要这个方法要抛出的异常。

异常也需要被 mapping。下面的 typemapping 做的也是这样的事情。这两个元素的节点的属

性和 operation 都是类似,对以一下大概就知道什么意思了。在这里也不多解释了。现在来

看测试代码。

Java 代码

package com.chnic.test;

import java.rmi.RemoteException;

import javax.xml.namespace.QName;

import javax.xml.rpc.Call;

import javax.xml.rpc.Service;

import javax.xml.rpc.ServiceException;

import javax.xml.rpc.ServiceFactory;

import javax.xml.rpc.encoding.TypeMapping;

import javax.xml.rpc.encoding.TypeMappingRegistry;

import org.apache.axis.encoding.ser.BeanDeserializerFactory;

import org.apache.axis.encoding.ser.BeanSerializerFactory;

import com.chnic.bean.UserBean;

import com.chnic.exception.NoSuchUserException;

public class TestException {

public static void main(String[] args){

???? String uri = "http://faults.samples";

???? String serviceName = "EmployeeInfoService";

???? ServiceFactory serviceFactory;

???? String url = "http://localhost:8080/axis/services/AxisExceptionTest";

???? try {

?????????? serviceFactory = ServiceFactory.newInstance();

?????????? QName serQ = new QName(uri,serviceName);

?????????? Service service = serviceFactory.createService(serQ);


TypeMappingRegistry registry = service.getTypeMappingRegistry();

TypeMapping map = registry.getDefaultTypeMapping();

QName employeeQName = new QName("urn:CustomerBean","User");

map.register(UserBean.class,employeeQName,

??? new BeanSerializerFactory(UserBean.class,employeeQName),

??? new BeanDeserializerFactory(UserBean.class,employeeQName));

QName faultQName = new QName("urn:CustomerFault","NoSuchUserFault");

map.register(NoSuchUserException.class,faultQName,

??? new BeanSerializerFactory(NoSuchUserException.class,faultQName),

??? new BeanDeserializerFactory(NoSuchUserException.class,faultQName));

Call call = service.createCall();

call.setTargetEndpointAddress(url);

call.setOperationName( new QName(uri,"checkUser") );

Object obj = call.invoke(new Object[]{ new Integer(3)});

UserBean user = (UserBean) obj;

System.out.println(user.getName());

} catch (ServiceException e) {

???? e.printStackTrace();

}catch(NoSuchUserException e){

???? e.printErrorMessage();

}catch (RemoteException e) {

???? e.printStackTrace();

}

}

}

看到第一个申明的 uri 了么?我们通过这个 uri 和 service 来取得对应的 service。 之后我们

用 TypeMappingRegistry 得到一个默认的 TypeMapping。在 map 里面映射我们的 bean。之后

和往常的代码一样没有特别的。invoke 唤起方法,返回 UserBean,并打出 UserBean 的 name。

值得注意的是后面的 catch 部分,我们 catch 了一个我们自己申明的 NoSuchUserException,

抓住这个异常之后打出我们要的错误信息。

因为 1 2 4 6 在数据库里都是有对应的数值的,所以当我要查找 ID 为 3 个 user 的 name 的时

候,service 就会返回一个 NoSuchUserException 给我。之后在本地抓住这个 exception 之后,

控制台打出了如下信息。

No such user: 3

如果输入的是 1 2 4 6 的话,则会返回 user 的名字。在这里就不测试了。各位自己可以试试。


利用 AXIS 开发 Webservice(五) —— 如何

传递文件

直入主题,我们先来编写服务器代码

Java 代码

package com.chnic.webservice;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import javax.activation.DataHandler;

public class FileTransfer {

public FileTransfer(){

}

public String transfer(DataHandler handler,String fileName){

?? ??String fullPath = "C:/" + fileName;

???? File file = new File(fullPath);

???? int flag = 0;

???? if(handler == null)

?????????? return "failure";

???? if(fileName == null)

?????????? return "failure";

try {

???? InputStream input = handler.getInputStream();

???? FileOutputStream fos = new FileOutputStream(file);

???? byte[] buffer=new byte[4096];

???? while((flag=input.read(buffer))!=-1){

????????? fos.write(buffer,flag);

???? }

???? input.close();

???? fos.close();

} catch (IOException e) {

???? e.printStackTrace();


return "failure";

}

return "Success";

}

}

transfer 方法是 service 端接受客户端传来文件的方法,方法里面有两个参数,第一个是

DataHandler。这个类可以看成是一个传送器,通过专门的序列化类将文件序列化。这个类

的具体方法可以查一下 SUN 的 API 文档。第二个参数是一个 String 字符串,参数名就可以

看出来,传递的是文件名。

这个方法从 DataHandler 得到一个输入流,从这个流里读出数据然后写到一个新文件里。这

些都是一些基本的 Java I/O 操作。应该不会太难。服务器代码编写完之后下来是 deploy.wsdd

发布文件。

Xml 代码

<deployment xmlns="http://xml.apache.org/axis/wsdd/"

???? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<service name="FileTransfer" provider="java:RPC">

???? <parameter name="className" value="com.chnic.webservice.FileTransfer"/>

???? <parameter name="allowedMethods" value="transfer"/>

???? <parameter name="scope" value="session"/>

<operation name="transfer"

???????????? qname="operNS:transfer"

???????????? xmlns:operNS="file"

???????????? returnQName="fileSend"

???????????? returnType="rtns:string"

???????????? xmlns:rtns="http://www.w3.org/2001/XMLSchema">

??? <parameter name="handler" type="tns:string"

??????????????? xmlns:tns="http://www.w3.org/2001/XMLSchema"/>

??? <parameter name="fileName" type="myns:DataHandler"

??????????????? xmlns:tns="http://www.w3.org/2001/XMLSchema"/>

</operation>

???????? <typeMapping qname="myns:DataHandler" xmlns:myns="http://fileTransfer.sample"

??????????? languageSpecificType="java:javax.activation.DataHandler"

??????????? serializer="org.apache.axis.encoding.ser.JAFDataHandlerSerializerFactory"

??????????? deserializer="org.apache.axis.encoding.ser.JAFDataHandlerDeserializerFactory"

??????????? encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

??? </service>

</deployment>

这个配置文件也不多介绍了,前几篇关于这几个元素的介绍已经比较清楚了,在这里有必要

提一下 org.apache.axis.encoding.ser.JAFDataHandlerSerializerFactory 这个类。前面序列化对

象的时候提到序列化不同对象的时候需要不同的工厂类,同样在序列化文件的时候就要用这


个工厂类。编写完一切,启动 tomcat 然后发布服务。

服务器端的代码已经编写完成了,现在轮到客户端代码。因为是个简单的 sample,所以做

的事情也比较简单就是把 C 盘根目录上的一个文件通过 webwervice 储存到 C 盘,当然文件

名要用另外一个。下面是客户端代码。

Java 代码

String fileName = "C:/sample.txt";

DataHandler dh=new DataHandler(new FileDataSource(fileName));

String endpoint="http://localhost:8080/axis/services/FileTransfer";

Service service=new Service();

Call call=(Call) service.createCall();

call.setTargetEndpointAddress(new java.net.URL(endpoint));

call.setOperationName(new QName(endpoint,"transfer"));

QName qnameattachment=new QName("http://fileTransfer.sample",

?????????? "myns:DataHandler");

call.registerTypeMapping(dh.getClass(),

?????????? qnameattachment,

?????????? JAFDataHandlerSerializerFactory.class,

?????????? JAFDataHandlerDeserializerFactory.class);

call.addParameter("s1",qnameattachment,ParameterMode.IN);

call.addParameter("s2",XMLType.XSD_STRING,ParameterMode.IN);

call.setReturnClass(String.class);

String ret=(String)call.invoke(new Object[] {dh,"aaaaaa.txt"});

System.out.println("transfer success....");

System.out.println(ret);

客户端通过 DataHandler 取得源文件,之后注册 DataHandler,注册两个输入参数和一个输出

参数。之后传入相关的参数。新文件也在 C 盘下面名字为 aaaaa.txt。运行测试代,控制台打

出两句话。

transfer success....

Success

说明测试成功,到 C 盘上也可以找到 aaaaa.txt 这个文件。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读