前面介绍了如何创建基于JAX-WS的webservice以及传递对象的一些相关内容,下面介绍下异步和Handler机制
1、异步
JAX-WS支持客户端的异步调用。在Server与普通的没多大区别,这里声明一个server服务:
@WebService(serviceName = "asynJaxWsService", endpointInterface = "org.ws.server.ws.chap3.AsynJaxWsService")
public class AsynJaxWsServiceImpl implements AsynJaxWsService {
@WebMethod
public @WebResult
Address asynAddress(String id) {
Address address = new Address();
address.setCity("chengdu");
address.setStreet("xxxx");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return address;
}
}
在发布服务后,生产客户端代码需要指定jaxws/bind文件,对于异步的bing.xml如下:
<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" wsdlLocation="http://localhost:8080/service/asynJaxWsService?wsdl" xmlns="http://java.sun.com/xml/ns/jaxws"> <bindings node="wsdl:definitions"> <enableAsyncMapping>true</enableAsyncMapping> </bindings> </bindings>
然后生产客户端代码命令:
wsimport -p org.sample.ws.client.ws.chap3 -keep http://localhost:8080/service/asynJaxWsService?wsdl -b ./binding.xml
在生产的客户端代码中提供了两种方式来异步调用:
public Response<AsynAddressResponse> asynAddressAsync(
@WebParam(name = "arg0", targetNamespace = "")
String arg0);
public Future<?> asynAddressAsync(
@WebParam(name = "arg0", targetNamespace = "")
String arg0,
@WebParam(name = "asyncHandler", targetNamespace = "")
AsyncHandler<AsynAddressResponse> asyncHandler);
可通过如下方式调用:
AsynJaxWsService jaxWs = new AsynJaxWsService_Service().getAsynJaxWsServiceImplPort();
jaxWs.asynAddressAsync("123", new AsyncHandler<AsynAddressResponse>() {
public void handleResponse(Response<AsynAddressResponse> res) {
try {
AsynAddressResponse response = res.get();
Address address = response.getReturn();
System.out.println(ToStringBuilder.reflectionToString(address, ToStringStyle.SHORT_PREFIX_STYLE));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
});
Response<AsynAddressResponse> response = jaxWs.asynAddressAsync("123");
while (!response.isDone()) {
System.out.println("not ending....");
}
AsynAddressResponse addressResponse = response.get();
Address address = addressResponse.getReturn();
System.out.println(ToStringBuilder.reflectionToString(address, ToStringStyle.SHORT_PREFIX_STYLE));
2、Handler机制
Handler机制(Filter、Interceptor)是很多框架必不可少的东西,甚至是构建在这个概念之上。在JAX-WS中提供了两种Handler机制:Logical Handlers and Protocol Handlers,简单地说前者处理业务上的后者处理访问等header层,更多信息见这里。我们这里要实现的一个简单授权访问的例子,就是基于ProtocolHandler(SOAPHandler),通过header中创建相应的节点来判断用户名或密码达到校验的目录。
首先来看看server端代码,自定义AuthHandler需要实现SOAPHandler:
public class AuthHandler implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext context) {
boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (!isRequest) {
try {
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
if (soapHeader == null) {
soapHeader = soapEnv.addHeader();
generateSOAPErrMessage(soapMsg, "No SOAP header.");
}
Iterator<?> it = soapHeader.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);
if (it == null || !it.hasNext()) {
generateSOAPErrMessage(soapMsg, "No header block for next actor.");
}
//a simple way to handle authorize:通过nodeName和value来判断
while (it.hasNext()) {
Node node = (Node) it.next();
if ("auth".equals(node.getNodeName()) && "aaaaaaaaaaaa".equals(node.getValue()))
return true;
}
generateSOAPErrMessage(soapMsg, "invalid user");
//tracking
soapMsg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true;//返回true是指向责任链中的下一个事物
}
public boolean handleFault(SOAPMessageContext context) {
return true;
}
public void close(MessageContext context) {
}
public Set<QName> getHeaders() {
return null;
}
private void generateSOAPErrMessage(SOAPMessage msg, String reason) {
try {
SOAPBody soapBody = msg.getSOAPPart().getEnvelope().getBody();
SOAPFault soapFault = soapBody.addFault();
soapFault.setFaultString(reason);
throw new SOAPFaultException(soapFault);
} catch (SOAPException e) {
}
}
}
在handler-chain.xml中声明
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class>org.ws.server.ws.chap7.AuthHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains>
server接口没多大变化,只需要添加@HandlerChain(file = "handler-chain.xml")指定配置文件
@WebService
@HandlerChain(file = "handler-chain.xml")
public class JaxWsHandlerServiceImpl implements JaxWsHandlerService {
@WebMethod
public String sayHello() {
return "Hello, Service";
}
}
发布即可完成server端的配置。
接下来看看客户端,通过命令生成客户端代码后,同样需要编写handler,需要处理就是在SOAP消息的Header中添加Node,起name和value分别对应server端的配置:
public class JaxWsClientHandler implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("Client : handleMessage()......");
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
//if this is a request, true for outbound messages, false for inbound
if (isRequest) {
try {
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
//if no header, add one
if (soapHeader == null) {
soapHeader = soapEnv.addHeader();
}
//add an node named "auth"
QName qname = new QName("http://handler.sws.com/", "auth");
SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(qname);
//set attribute value
soapHeaderElement.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
soapHeaderElement.addTextNode("aaaaaaaaaaaa");
soapMsg.saveChanges();
//tracking
soapMsg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
return true;
}
public void close(MessageContext context) {
}
public Set<QName> getHeaders() {
return null;
}
}
其他工作与服务端一样,编写handler-chain.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class>org.sample.ws.client.ws.chap7.main.JaxWsClientHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains>
同时在生成的客户端代理类中需要加入该hander-chain,看起来是这样的:
@WebServiceClient(name = "JaxWsHandlerServiceImplService", targetNamespace = "http://impl.chap7.ws.server.ws.org/", wsdlLocation = "http://localhost:8080/service/jaxWsAuthSrevice?wsdl")
@HandlerChain(file = "handler-chain.xml")
public class JaxWsHandlerServiceImplService
extends Service
这样就完成了一个简单的实例,按照普通的调用即可:
JaxWsHandlerServiceImpl service = new JaxWsHandlerServiceImplService().getJaxWsHandlerServiceImplPort();
System.out.println(service.sayHello());
如果没有auth节点或者value(密码)不对就会抛出异常如:
at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:111)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:108)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:107)
at $Proxy29.sayHello(Unknown Source)
at org.sample.ws.client.ws.chap7.main.JaxWsHandlerServiceMain.main(JaxWsHandlerServiceMain.java:15)