客户端也使用spring集成JAX-WS。但是有一个问题需要处理!
想在SOAP消息中加入HEADER该怎样加入呢,使用Handler!
之前使用handler都是通过在实现类上使用注解@HandlerChain(file="handler-chain.xml")进行标注引入的
//声明handler的位置
@HandlerChain(file="handler-chain.xml")
public class StudentWSService
extends Service
{
...
}
然后手动创建这个实现类的对象,这样自然能讲handler加入到程序运行逻辑中。
IStudentWSService iservice = new StudentWSService().getStudentServicePort();
但是,将JAX-WS与spring集成后,使用的实现类是由spring提供的一个代理来完成,这将如何做呢???
通过JaxWsPortProxyFactoryBean的最上层父类LocalJaxWsServiceFactory中的属性handlerResolver来定义!
编写Handler
package com.hqh.student.ws.handler;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class LicenseHandler implements SOAPHandler<SOAPMessageContext> {
public void close(MessageContext ctx) {
}
public boolean handleFault(SOAPMessageContext ctx) {
return false;
}
public boolean handleMessage(SOAPMessageContext context) {
try {
Boolean out = (Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(out) {
System.out.println("message outing...");
SOAPEnvelope soapEnv = context.getMessage().getSOAPPart().getEnvelope();
SOAPBody soapBody = soapEnv.getBody();
//获取当前在调用服务的哪个方法
String localName = soapBody.getFirstChild().getLocalName();
// System.out.println(localName);
if(!"getStudent".equals(localName)) {
return true;
}
//如果是getStudent,则加入头信息
SOAPHeader soapHeader = soapEnv.getHeader();
if(soapHeader==null) {
soapHeader = soapEnv.addHeader();
}
//创建QName
String nameSpace = "http://ws.student.hqh.com";//wsdl的targetNamespace
String part = "license";//message的name,必须与wsdl中定义的一致
String prefix = "ns";//前缀
QName headerQName = new QName(nameSpace, part, prefix);
//将QName加入Header,并设定值
soapHeader.addHeaderElement(headerQName).setValue("admin");
} else {
System.out.println("message come back...");
}
return true;
} catch (SOAPException e) {
e.printStackTrace();
}
return false;
}
public Set<QName> getHeaders() {
return null;
}
}
编写HandlerResolver
package com.hqh.student.ws.handler;
import java.util.ArrayList;
import java.util.List;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
import org.springframework.stereotype.Service;
import com.sun.xml.ws.util.JAXWSUtils;
/**
* 将新的HandlerList返回,由spring提供的代理服务进行调用
* 这样就达到了使用Handler的目的
* 怎样通过paramPortInfo获取到已有的Handler呢???
*/
@Service("soapHeaderHandler")
public class SOAPHeaderHandler implements HandlerResolver {
private List<Handler> newHandlerList = null;
public SOAPHeaderHandler() {
newHandlerList = new ArrayList<Handler>();
newHandlerList.add(new LicenseHandler());
}
public List<Handler> getHandlerChain(PortInfo paramPortInfo) {
return newHandlerList;
}
}
让spring注入handler到其代理服务中
在spring为JAX-WS提供的代理bean中配置Handler(关键),这样就能将Handler注入到代理对象中了!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:ws="http://jax-ws.dev.java.net/spring/core"
xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core/spring-jax-ws-core.xsd
http://jax-ws.dev.java.net/spring/servlet http://jax-ws.dev.java.net/spring/servlet/spring-jax-ws-servlet.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.hqh.client"/>
<!-- 声明需要被spring进行注解扫描 -->
<context:component-scan base-package="com.hqh.student.ws.handler"/>
<!-- 利用spring完成webservice服务的注入! -->
<!-- Accessing web services using JAX-WS -->
<bean id="wsService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="com.hqh.student.ws.IStudentWSService"/>
<property name="wsdlDocumentUrl" value="http://localhost:9999/server/jaxws-spring?wsdl"/>
<property name="namespaceUri" value="http://ws.student.hqh.com"/>
<property name="serviceName" value="StudentWSService"/>
<property name="portName" value="studentServicePort"/>
<!-- 将处理头信息的Handler注入到JaxWsPortProxyFactoryBean中-->
<property name="handlerResolver" ref="soapHeaderHandler"></property>
</bean>
<!-- 依赖注入 -->
<bean id="client" class="com.hqh.client.ws.controller.ClientController">
<property name="studentWsService" ref="wsService"></property>
</bean>
</beans>
服务端增加异常类,如果调用服务端的getStudent()没有头信息或者头信息中的license错误,则抛出异常
package com.hqh.student.exception;
/**
* 处理webservice的异常最好继承Exception,而不是RuntimeException
* 该Exception就是wsdl中新增的自定义Exception
* @author lenovo
*
*/
public class StudentWsException extends Exception {
public StudentWsException() {
super();
}
public StudentWsException(String message, Throwable cause) {
super(message, cause);
}
public StudentWsException(String message) {
super(message);
}
public StudentWsException(Throwable cause) {
super(cause);
}
}
在wsdl中增加异常消息
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://ws.student.hqh.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="StudentWSService" targetNamespace="http://ws.student.hqh.com"> <!-- 指定schema --> <wsdl:types> <xsd:schema targetNamespace="http://ws.student.hqh.com"> <xsd:include schemaLocation="student.xsd"/> <!-- 增加头信息(for HEADER) --> <xsd:element name="license" type="xsd:string"/> <!-- 增加异常 --> <xsd:element name="StudentWsException" type="tns:StudentWsException"></xsd:element> <xsd:complexType name="StudentWsException"> <xsd:sequence> <xsd:element name="errorMsg" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema> </wsdl:types> <!-- 增加异常消息 --> <wsdl:message name="StudentWsException"> <wsdl:part name="fault" element="tns:StudentWsException"/> </wsdl:message> <!-- 为license创建新的message(for HEADER) --> <wsdl:message name="license"> <wsdl:part name="license" element="tns:license"></wsdl:part> </wsdl:message> <!-- 指定方法 --> <wsdl:message name="getStudent"> <wsdl:part element="tns:getStudent" name="parameters" /> </wsdl:message> <wsdl:message name="getStudentResponse"> <wsdl:part element="tns:getStudentResponse" name="parameters" /> </wsdl:message> <wsdl:message name="list"> <wsdl:part element="tns:list" name="parameters" /> </wsdl:message> <wsdl:message name="listResponse"> <wsdl:part element="tns:listResponse" name="parameters" /> </wsdl:message> <wsdl:message name="listReward"> <wsdl:part element="tns:listReward" name="parameters" /> </wsdl:message> <wsdl:message name="listRewardResponse"> <wsdl:part element="tns:listRewardResponse" name="parameters" /> </wsdl:message> <!-- 指定接口 --> <wsdl:portType name="IStudentWSService"> <wsdl:operation name="getStudent"> <wsdl:input message="tns:getStudent" /> <wsdl:output message="tns:getStudentResponse" /> <!-- 异常 --> <wsdl:fault name="StudentWsException" message="tns:StudentWsException"></wsdl:fault> </wsdl:operation> <wsdl:operation name="list"> <wsdl:input message="tns:list" /> <wsdl:output message="tns:listResponse" /> </wsdl:operation> <wsdl:operation name="listReward"> <wsdl:input message="tns:listReward" /> <wsdl:output message="tns:listRewardResponse" /> </wsdl:operation> </wsdl:portType> <!-- 指定接口中的方法的参数编码格式 --> <wsdl:binding name="studentSOAP" type="tns:IStudentWSService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="getStudent"> <wsdl:input> <!-- add方法的header中加入license消息 (for HEADER) --> <soap:header use="literal" part="license" message="tns:license"/> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> <!-- 异常 --> <wsdl:fault name="StudentWsException"> <soap:fault name="StudentWsException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="list"> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> <wsdl:operation name="listReward"> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <!-- 指定服务名称、端口、地址 --> <wsdl:service name="StudentWSService"> <wsdl:port binding="tns:studentSOAP" name="studentServicePort"> <soap:address location="http://localhost:8080/student-web/ws/" /> </wsdl:port> </wsdl:service> </wsdl:definitions>
服务端的接口和实现类抛出异常
@WebService(name = "IStudentWSService", targetNamespace = "http://ws.student.hqh.com")
public interface IStudentWSService {
/**
*
* @param number
* @return
* returns com.hqh.student.ws.Student
*/
@WebMethod
@WebResult(name = "stu", targetNamespace = "")
@RequestWrapper(localName = "getStudent", targetNamespace = "http://ws.student.hqh.com")
@ResponseWrapper(localName = "getStudentResponse", targetNamespace = "http://ws.student.hqh.com")
public Student getStudent(
@WebParam(name = "number", targetNamespace = "")
String number,
@WebParam(name="license", header=true) String license) throws StudentWsException;
@WebService(endpointInterface="com.hqh.student.ws.IStudentWSService",
serviceName="StudentWSService",
portName="studentServicePort",
targetNamespace="http://ws.student.hqh.com",
wsdlLocation="/WEB-INF/wsdl/student.wsdl")
//该对象交由spring管理,studentWsService即为该实现类在bean容器中的name
@Component("studentWsService")
public class StudentWSServiceImpl implements IStudentWSService{
// private static final BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
// public StudentWSServiceImpl() {
// if(studentService==null) {
// studentService = factory.getBean(StudentService.class);
// }
// }
//自动注入
@Resource
private StudentService studentService;
@Override
public Student getStudent(String number, String license) throws StudentWsException {
// studentService = (StudentService) WebUtil.getBean(StudentService.class);
// studentService = (StudentService) BeanFactoryUtil.getBean(StudentService.class);
//根据license进行处理
System.out.println("client send license:"+license);
if(license==null || "".equals(license)) throw new StudentWsException("SOAP消息HEADER不能为空!");
if(!license.equals("admin")) throw new StudentWsException("SOAP消息HEADER携带的license错误!");
return studentService.getStudent(number);
}
从新生成客户端的java文件,调用服务时捕获异常消息
package com.hqh.client.ws.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.hqh.student.ws.IStudentWSService;
import com.hqh.student.ws.Student;
import com.hqh.student.ws.StudentWsException_Exception;
@Controller
public class ClientController {
/**
* 容器初始化第一次,会调用setters进行注入,但是第2次开始,ClientController变为一个新的对象
* 导致studentWsService为NULL,所以,这里使用static来保存第一次初始化好webservice接口的代理对象
* 这里是使用配置文件的方式利用spring提供的配置示例进行注入的
* 用注解注入又该怎样做呢?
*/
private static IStudentWSService studentWsService;
//通过setters方法进行注入!
public void setStudentWsService(IStudentWSService wsService) {
studentWsService = wsService;
}
@RequestMapping(value="/getStudent",method=RequestMethod.GET)
public String getStudent() {
return "index";
}
@RequestMapping(value="/getStudent",method=RequestMethod.POST)
public String getStudent(String number,Model model) {
Student stu = null;
try {
stu = studentWsService.getStudent(number);
} catch (StudentWsException_Exception e) {
//调用服务如果发生异常,将异常显示到页面中
model.addAttribute("error", e.getMessage());
return "index";
}
if(stu==null) {
model.addAttribute("error", "不存在!");
} else {
model.addAttribute(stu);
}
return "index";
}
}
客户端调用getStudent()
如果没有传递Header,抛出“SOAP消息HEADER不能为空!”异常;
如果传递了Header,但license不是"admin",抛出"SOAP消息HEADER携带的license错误!"异常;
以上两项都通过验证,服务端才继续为客户端提供服务。