今天进入AXIS之四,如何抛出一个你的自定义异常。本来想连传文件一起介绍的。后来感觉一篇blog里太多的内容也不太好,看起来太辛苦,还是慢慢来,废话不多进入正题。
上一篇介绍了如果在Server和Client端传递一个自己的对象。有些人也许会问传递异常行不行?答案是可以。只不过传递异常的配置要稍微复杂一些。空口无凭,我还是用点代码来说明。今天的例子稍微复杂点,用一下数据库(MySQL)。首先创建表和输入测试数据。
- create table users(id integer primary key, name varchar(20) not null);
-
- insert into users values(1, 'Lincoln'),(2, 'Michael'),(4, 'Mahone'),(6, 'Sara');
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。
- 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;
- }
- }
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的代码。
- 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;
- }
- }
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的代码。
- 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();
- }
- }
- }
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文件,这次的稍微有点点复杂。
- <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>
<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都是类似,对以一下大概就知道什么意思了。在这里也不多解释了。现在来看测试代码。
- 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();
- }
- }
- }
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的名字。在这里就不测试了。各位自己可以试试。