I 使用 AXIS(版本 1.4)的方法 邢飞 先配置好 JDK 和Tomcat。本文中使用的是JDK 5.0(源代码是 1.4 的)。Tomcat 是5.0.28。 下载 AXIS 1.4,地址 http://ws.apache.org/ 安装 AXIS 解压开 axis1_4.zip ,将 axis1_4/webapps/axis 拷贝到 $TOMCAT_HOME/webapps/ 下启动 tomcat,打开页面http://localhost:8080/axi s 页面正常,表示 axis 安装正确。 II 编写服务代码,如 需要发布的方法为 deposit , withdraw , getBalance 将编译好的 Account.class 文件放在文件夹 $TOMCAT_HOME/webapps/axis/WEBINF/classes/com/hcycom/n7 中 如果使用 IDE,可以将输出文件夹设为 $TOMCAT_HOME/webapps/axis/WEBINF/classes /* * Account.java */ package com.hcycom.n7; public class Account { public static int balance = 5000; // 存款 public String deposit (int amount) { String ret = "success"; if(amount > 0){ balance += amount; ret = "success"; } else { ret = "failure"; } return ret; } // 取款 public String withdraw (int amount) { String ret = "success"; if(amount > 0 && amount <= getBalance()){ balance = amount; ret = "success"; } else { ret = "failure"; } return ret; } // 获取余额 public int getBalance (){ return balance; } } III 编写部署描述符/home/xingfei/axis/ deploy.wsdd 内容如下 注意红色部分, account 表示服务的名称 java:RPC 表示调用服务的方法 com.hcycom.n7.Account 表示服务要调用的类的名称 * 表示要公布的方法,也可以公布一个类的某几个方法,使用逗号隔 IV 部署服务 account , 将axis 所需 jar 文件全部加到 classpath 中,执行以下命令 得到结果 Done processing 表示部署成功 也可以在 IDE 中使用如下方法部署 public class Deploy { public static void main(String[] args) { String[] arguments = { /home/xingfei/axis/ deploy.wsdd" }; org.apache.axis.client. AdminClient.main(arguments); } } % java org.apache.axis.client.AdminClient \ /home/xingfei/axis/ deploy.wsdd V 编写客户端调用代码,如下 其中红色部分为实际调用 account 服务的代码 package com.hcycom.n7; import javax.xml.namespace.QName; import org.apache.axis.client.Call; import org.apache.axis.client.Service; public class AccountClient { public static void main(String[] args) { String endpoint = "http://localhost:8080/axis/services/account"; try { Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress( new java.net.URL(endpoint) ); call.setOperationName(new QName("http://soapinterop.org/", "deposit ")); // 上面设置了操作为 deposit,这里意思是存 300 String ret = (String)call.invoke(new Object[]{new Integer(300)}); if(ret.equals("success")) { System.out.println("Deposit operation has been successfully done"); call.setOperationName(new QName("http://soapinterop.org/", "getBalance ")); // 操作为获取余额,这里得到整型的余额数 Integer balance = (Integer)call.invoke(new Object[0]); System.out.println("Now the balance is " + balance.intValue()); } else if(ret.equals("failure")) { System.out.println("Deposit operation has done something wrong"); } } catch (Exception e) { System.err.println(e.toString()); } } } VI 以上所写的,只适用于简单情况: 输入参数和输出结果都是基本类型 但是如果输入参数和输出结果中有我们自定义的复杂类型的话,就会出错。 因为客户端和服务器端使用的是 SOAP 消息进行通信。发送方将 Java 对象序列化为 XML,而接 收方又将 XML 反序列化成 Java 对象。双方应该知道Java 对象和 XML 之间是如何对应的,才能 正确的工作。 AXIS 可以在配置文件中使用, 来说明 Java 对象如何序列化 和反序列化。下面是一个例子: 其中 type 指我们要处理的 Java 对象。 serializer ,deserializer 指将这个 Java 对象进行序列化和反序列化的“工具”类 qname 指的是这个 Java 对象的命名空间 这些可以自己写,也可以由 AXIS 自动帮我们完成,下面介绍一个综合的例子,看看 AXIS 如何 为我们自动化的生成这些代码的。 例子是一个非常简单的用户管理服务。可以添加,删除和查看用户的信息。 用户有 name,age,sex 和contact 字段,而contact 又是一个 bean,由phone 和email 两 个字段。UserManager 是服务的定义,它定义了对 User 的操作。 以下将详细说明。 VII 服务定义 你要向外提供什么样的服务,可以先写一个 interface 来描述它。这个 interface 必需继承 自java.rmi.Remote ,而且每个方法必需抛出 java.rmi.RemoteException 。本例中的服 务定义为: 注意,红色部分是必需的,不然 AXIS 将不认识这个服务 package com.hcycom.user; import java.rmi.Remote; import java.rmi.RemoteException; public interface UserManager extends Remote { /** * add a user * 输入部分有复杂类型 */ public int addUser(User user) throws RemoteException ; /** * delete a user */ public int deleteUser(String name) throws RemoteException ; /** * get all the users * 输出为复杂类型 */ public User[] getAllUsers() throws RemoteException ; /** * get a user by name * 输出为复杂类型 */ public User getUser( String name) throws RemoteException ; } VIII 其中 User 的定义为,一个标准的 JavaBean Contact 定义,也是一个标准的 JavaBean 这两个类都是标准的 JavaBean: 都有默认的构造方法和符合命名规范的 getters 和setters package com.hcycom.user; public class Contact { private String phone; private String email; public Contact() { super(); } ...... getters and settters } package com.hcycom.user; public class User { private String name; private int age; private int sex; private Contact contact; public User() { super(); } ...... getters and setters } IX 生成 WSDL 文件 WSDL 是对服务的描述,比如服务的名称,能够进行的操作,和输入/输出的参数。 axis 所需的 jar 文件全都包含到 classpath 里边,然后执行如下命令 参数含义如下 o,output 指定输出的 WSDL 文件名。若没有指定,一个适当的默认 WSDL 文件名将被写到当前目录。 l,location 指定 Web 服务位置的 URL。最后的斜线或反斜线后的名字是服务端口(service port)的名字 (除非被s 选项覆盖)。服务端口的地址位置属性被分配为该指定值。 P,portTypeName 指定 portType 元素的名字。如果未指定,则使用 classofportType 的名字。 b,bindingName 指定 binding 元素的名字。若未指定,使用值 –servicePortName + "SOAPBinding" 。 n,namespace 指定生成的 WSDL 的目标名称空间。 p,PkgtoNS 指定包结构到命名空间的映射。若遇到一个没有指定命名空间映射的包,则 Java2WSDL 将生成 一个适当的命名空间名。这个选项可以指定多次。 y,style 指示 WSDL 文档的风格,有 DOCUMENT, RPC 和WRAPPED 三种。默认为 RPC。 com.hcycom.user.UserManager 就是服务定义接口 执行完上述命令之后会生成一个 UserManager.wsdl 文件 (注意:p 和后边的参数之间不能有空格,这是本人的经验) java cp .:$AXIS_CLASSPATH org.apache.axis.wsdl.Java2WSDL o UserManager.wsdl P UserManagerPortType b UserManagerSoapBinding l http://localhost:8080/axis/services/UserManager n http://www.hcycom.com/n7/User pcom.hcycom.user=http://www.hcycom.com/n7/User y rpc com.hcycom.user.UserManager X 生成服务器端和客户端代码 有了 WSDL 文件我们就可以生成服务器端和客户端的代码框架,然后再将我们具体的业务逻辑填 进去就可以了。 axis 所需的 jar 文件全都包含到 classpath 里边,执行如下命令 参数含义如下 s,serverside emit serverside bindings for web service p,package override all namespace to package mappings, use this package name instead c,implementationClassName custom name of web service implementation UserManager.wsdl 上一步产生的 wsdl 文件 执行完上述命令将产生如下文件(位于/home/xingfei/axis/com/hcycom/user) deploy.wsdd undeploy.wsdd 部署(反部署)描述符文件 Contact.java User.java 和前面自己写的一样,用哪个都可以 UserManagerPortType.java 和接口 UserManager 一样,就是名字变了。本文中用这个类。 UserManagerImpl.java 实现 UserManagerPortType 的类。具体的逻辑还得自己去填充。 UserManagerPortTypeService.java 用于获取 UserManagerPortType ,是一个接口 UserManagerPortTypeServiceLocator.java UserManagerPortTypeService 的实现 UserManagerSoapBindingStub.java 方便客户端调用服务的 Stub 类 java cp .:$AXIS_CLASSPATH org.apache.axis.wsdl.WSDL2Java s p com.hcycom.user c com.hcycom.user.UserManagerImpl UserManager.wsdl XI 实现服务 前面只是定义了服务,没有具体实现,下面要将具体实现,填入到 UserManagerImpl.java 中。因为这只是个例子,所以简单的使用一个 HashMap 来保存用户信息。具体情况下可能需要 将用户信息保存到数据库等等持久设备中。代码如下: package com.hcycom.user; import java.rmi.RemoteException; import java.util.HashMap; import java.util.Iterator; public class UserManagerImpl implements com.hcycom.user.UserManagerPortType{ private static HashMap users = new HashMap(); public synchronized int addUser(com.hcycom.user.User in0) throws RemoteException { if(users.containsKey(in0.getName())) { throw new RemoteException("user already exists"); } users.put(in0.getName(), in0); int ret = users.size(); return ret; } public synchronized int deleteUser(java.lang.String in0) throws RemoteException { if(!users.containsKey(in0)) { throw new RemoteException("user does not exist"); } users.remove(in0); int ret = users.size(); return ret; } public synchronized User[] getAllUsers() throws RemoteException { int num = users.size(); User[] userList = new User[num]; int idx = 0; for(Iterator it = users.values().iterator();it.hasNext();) userList[idx++] = (User)it.next(); return userList; } XII 这里只是示例,写的很简单。因为 web 服务是运行在多线程的情况下,实际中要注意线程安全。 部署服务 UserManager 先打开 Tomcat ,然后 axis 所需的 jar 文件全都包含到 classpath 里边,执行如下命令 得到结果 Done processing 表示部署成功 这个和前面部署是一样的,打开浏览器,访问 http://localhost:8080/axis/servlet/AxisServlet 可以看到 UserManager 服务已经成功部署,它有 4个可以调用的方法。 public synchronized User getUser(java.lang.String in0) throws RemoteException { if(!users.containsKey(in0)) { throw new RemoteException("user doen not exist"); } User user = (User)users.get(in0); return user; } } java cp .:$AXIS_CLASSPATH org.apache.axis.client.AdminClient /path/to/deploy.wsdd XIII 编写客户端 客户端可以用两种方式,第一是使用 AXIS 刚刚生成的 Stub,第二是使用动态调用。下面用代 码来说明。 1.使用 Stub,代码如下: import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com.hcycom.user. *; public class UserClientUsingStub { static UserManagerPortTypeService getUserManagerPortTypeService() { return new UserManagerPortTypeServiceLocator(); } public static void main(String[] args) { UserManagerPortTypeService service = getUserManagerPortTypeService(); try { UserManagerPortType userManager = service.getUserManager(); User user = new User(); user.setAge(20); user.setName("James Gosling"); user.setSex(0); Contact contact = new Contact(); contact.setEmail("james@sun.com"); contact.setPhone("123456789"); user.setContact(contact); userManager.addUser(user); user.setAge(30); user.setName("Scott McNealy"); user.setSex(0); contact.setEmail("scott@sun.com"); contact.setPhone("987654321"); user.setContact(contact); userManager.addUser(user); User[] users = userManager.getAllUsers(); for(int i=0;i