前面介绍了如何创建基于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)