1. Web服务示例:订单处理
我之所以选择“订单处理”作为示例,是因为它比较接近实际的商业用例。该Web服务能够处理,更新一个给定的订单。为了达到这个目的,它必须具有两个方法:processOrder和updateOrder。方法processOrder具有一个IN参数orderID和一个作为OUT参数的Order(订单)对象。processOrder方法返回一个状态字符串。方法updateOrder把一个Order(订单)对象作为INOUT参数,它更新orderDate,并且把Order对象返回给客户端。由于这两个方法都用到了一个复杂数据类型Order,更确切的说,它被用作为OUT/INOUT参数,所以需要开发一个Holder类。下面的清单1和清单2分别给出了Order类和相应的Holder类(为了说明,全部代码都放在sample包中):
清单1:Order类
package sample;
public class Order {
// ID for order
private String orderID = null;
// date of order
private String orderDate= null;
// getter methodspublic String getOrderID() {
return orderID;
}
public String getOrderID() {
return orderID;
}
// setter methods
public void setOrderID(String orderID) {
this.orderID = orderID;
} public void setOrderDate(String orderDate) {
this. orderDate = orderDate;
}
}
清单2:Order类相应的Holder类
// Note that holder class is in the holders package and its name
// is derived by adding Holder as a suffix to 'Order', as per
// the JAX-RPC specification.
package sample.holders;
public class OrderHolder {
// Order's object
public Order value = null;
// default constructor
public void OrderHolder () { }
// constructor, which takes value as a parameter
public void OrderHolder (Order value) {
this.value=value;
}
}
现在,我们来开发具有上述功能的Web服务。清单3给出了相应的代码。
清单3:订单处理Web服务
package sample;
public class OrderProcessingService {
// Method 1: processes a order given ID as input and
// return status and Order object as an OUT parameter
public String processOrder(String orderID,
OrderHolder orderHolder ) {
String status = "pending";
// perform business logic here
// for simplicity just filling the Order object
Order order = new Order();
order.setOrderID(orderID);
order.setOrderDate("03 March 2003");
// set the Holder value to the order.
orderHolder.value = order;
//set the status
status = "complete" ;
return status;
}
// Method 2: updates a order given Order as an INOUT
// parameter and returns status.
public String updateOrder(OrderHolder orderHolder) {
String status = "pending";
// perform update here
Order order = orderHolder.value;
order.setOrderDate("03 April 2003");
// Note that orderID is not changed.
// It will be same as the passed one.
// set the Holder value to the order.
orderHolder.value = order;
//set the status
status = "complete" ;
return status;
}
}
至此,我们已经完成Web服务的开发,下一步就是编译,把它部署到Tomcat-Axis平台上去。编译后,我们需要用部署描述文件把上述的Web服务部署到Tomcat-Axis。
清单4:部署文件deploy.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name=" OrderProcessingService" provider="java:RPC">
<parameter name="className"
value="sample.OrderProcessingService "/>
<parameter name="allowedMethods" value="*"/>
<operation name="processOrder">
<parameter name="arg1" mode="IN"/>
<parameter name="arg2" mode="OUT"/>
</operation>
<operation name="updateOrder">
<parameter name="arg1" mode="INOUT"/>
</operation>
</service>
</deployment>
上述的部署描述文件实际上是让服务器知道有关该Web服务的一些信息,例如公开的方法,期望的参数以及返回值类型等。部署OrderProcessingService,我们需要传递参数“deploy.wsdd”,调用Axis admin服务。运行在同一服务器上的admin服务将处理描述文件,部署该Web服务,至此,它可以被客户端调用了。
在与deploy.wsdd文件相同的目录下运行下列命令:
java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient
-lhttp://localhost:8080/axis/services/AdminService deploy.wsdd
其中,AXISCLASSPATH用于设置Axis环境(详情请参考axis installation guide)。
可以通过下面的地址来访问OrderProcessing服务:http://<your_machine_name>:<port-num>/<contextURI>/<serviceURI>.
对我们的示例来说,地址如下:
http://localhost:8080/axis/services/OrderProcessing
2. Web服务OrderProcessing的客户端
动态客户端
动态客户端类似于用反射APIs(reflection APIs)查找,调用Java类的方法.
这里,所有的信息,例如目标端点(target endpoint),方法参数等等都必须明确的设定。清单5所列出的代码展示了怎样编写一个调用Web服务OrderProcessing的updateOrder方法的动态客户端。
清单5:动态客户端
package sample.client;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.encoding.*;
import javax.xml.namespace.QName;
import java.util.*;
import sample.*;
/**
* This class illustrates how to use the JAX-RPC API to invoke
* the Order Processing Web service dynamically
*/
public class DynamicClient {
public static void main(String[] args) throws Exception {
// create service factory
ServiceFactory factory = ServiceFactory.newInstance();
// define qnames
String targetNamespace = "OrderProcessingService";
QName serviceName = new QName(targetNamespace,
"OrderProcessingService");
QName portName = new QName(targetNamespace,
"OrderProcessingService");
QName operationName = new QName(targetNamespace, "updateOrder");
// create service
Service service = new Service();
Call call = (Call) service.createCall();
Qname qn = new Qname(targetNamespace, "OrderHolder");
call.registerTypeMapping(OrderHolder.class, qn,
new org.apache.axis.encoding.ser.BeanSerializerFactory
(OrderHolder.class, qn),
new org.apache.axis.encoding.ser.BeanDeserializerFactory
(TicketHolder.class, qn));
// set port and operation name
call.setPortTypeName(portName);
call.setOperationName(operationName);
// add parameters
call.addParameter( "arg1", serviceName, ParameterMode.INOUT );
call.setReturnType( XMLType.XSD_STRING );
Order order = new Order ();
order.setOrderID("Order001");
order.setOrderDate("03 March 2003");
// set end point address
call.setTargetEndpointAddress(
"http://localhost:8080/axis/services/OrderProcessing");
// Invoke the WebService
String result = (String) call.invoke( new Object[] { order } );
System.out.println("result : " +result);
Map outparams = call.getOutputParams();
System.out.println("Got the outparams");
}
3. 运行客户端
用下列命令运行客户端:
<Prompt>java -cp %AXISCLASSPATH% sample.client.DynamicClient
结果如下:
得到输出参数(如在开发客户端时所提到的一样)。
4. 结论
本文试图揭开Web服务的神秘面纱,表达一种使用Apache开放源代码的Axis工具开发基于JAX-RPC的Web服务是多么的简单和经济适用。该文还详细阐述了一种“怎样开发”基于JAX-RPC的Web服务的方式。这种开发方式给开发者充分的自由去编写Web服务和客户端,隐藏了以有线XML(on-the-wire XML)格式序列化对象的所有复杂细节。对开发人员来说,它看起来只不过是Java方法调用。本文系列的下一部分,我将讲解Web服务的其他方法和技术。
5. 参考资料