JAX-WS Dynamic Proxy Client Example
1. Introduction
Java API for XML Web Services (JAX-WS) is a Java programming language for creating web services, particularly SOAP services.
JAX-WS wsimport
generates java stubs and binds the web service during the compile time. The generated client code binds to the WSDL at a specific service implementation. Clients need to regenerate the stubs when WSDL is updated.
JAX-WS provides libraries to construct services during run-time and invoke service’s operations via Dispatch
interface. Dynamic web service clients don’t need to change when WSDL is updated.
In this example, I will demonstrate how to build a dynamic proxy web service client.
2. JAX-WS Server Application
Create a JAX-WS server application in three steps:
- Create an
Interface
and annotate it with@WebService
- Create an implementation class for the
Interface
and annotate it with@WebService(endpointInterface="")
- Create an
Endpoint
to publish the service
Refer to my other article for step-by-step instructions.
This step will starts a server with two services: http://localhost:9980/bookSerer.wsdl
and http://localhost:9990/mathSerer.wsdl.
http://localhost:9980/bookSerer.wsdl
file content.
bookServer.wsdl
<?xml version="1.0" encoding="UTF-8"?><!-- Published by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e. --><!-- Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e. --> <definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://jcg.demo.zheng/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://jcg.demo.zheng/" name="BookServiceImplService"> <types> <xsd:schema> <xsd:import namespace="http://jcg.demo.zheng/" schemaLocation="http://localhost:9980/bookServer?xsd=1"></xsd:import> </xsd:schema> </types> <message name="getBook"> <part name="parameters" element="tns:getBook"></part> </message> <message name="getBookResponse"> <part name="parameters" element="tns:getBookResponse"></part> </message> <portType name="BookService"> <operation name="getBook"> <input wsam:Action="http://jcg.demo.zheng/BookService/getBookRequest" message="tns:getBook"></input> <output wsam:Action="http://jcg.demo.zheng/BookService/getBookResponse" message="tns:getBookResponse"></output> </operation> </portType> <binding name="BookServiceImplPortBinding" type="tns:BookService"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"></soap:binding> <operation name="getBook"> <soap:operation soapAction=""></soap:operation> <input> <soap:body use="literal"></soap:body> </input> <output> <soap:body use="literal"></soap:body> </output> </operation> </binding> <service name="BookServiceImplService"> <port name="BookServiceImplPort" binding="tns:BookServiceImplPortBinding"> <soap:address location="http://localhost:9980/bookServer"></soap:address> </port> </service> </definitions>
3. Static Web Service Client
Build a static web service client with wsimport.
This step creates two client applications: BookServiceClient
and MathServiceClient
.
Refer to my other article for step-by-step instructions.
4. Dynamic Proxy Web Service Client
There are three steps to build a dynamic proxy web client:
- Parse the web service WSDL into
ServiceDetail
data model - Construct the
Service
viaServiceDetail
- Invoke the service’s operation via
Dispatch
method
4.1. Data Model
Create ServiceDetail
class to track web service detail.
ServiceDetail.java
package jcg.demo.jaxws.client.model; import java.util.List; public class ServiceDetail { private String wsdl; private String nameSpace; private String prefix; private String serviceName; private String portName; private List operations; public ServiceDetail(String wsdl) { super(); this.wsdl = wsdl; } public String getWsdl() { return wsdl; } public void setWsdl(String wsdl) { this.wsdl = wsdl; } public String getNameSpace() { return nameSpace; } public void setNameSpace(String nameSpace) { this.nameSpace = nameSpace; } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getPortName() { return portName; } public void setPortName(String portName) { this.portName = portName; } public List getOperations() { return operations; } public void setOperations(List operations) { this.operations = operations; } }
Create Operation
class to track web service’s operation detail.
Operation.java
package jcg.demo.jaxws.client.model; import java.util.List; public class Operation { private String name; private List parameterNames; public Operation(String name) { super(); this.name = name; } public Operation(String name, List parameterNames) { super(); this.name = name; this.parameterNames = parameterNames; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getParameterNames() { return parameterNames; } public void setParameterNames(List parameterNames) { this.parameterNames = parameterNames; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Operation other = (Operation) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
4.2. Parse WSDL
Parse the WSDL by extracting the ServiceDetail
out of the Document
object.
4.2.1 ParseWsdlService
Create ParseWsdlService
to parse WSDL into ServiceDetail
ParseWsdlService.java
package jcg.demo.jaxws.client; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import jcg.demo.jaxws.client.model.Operation; import jcg.demo.jaxws.client.model.ServiceDetail; public class ParseWsdlService { private static final String PORT = "port"; private static final String SERVICE = "service"; private static final String COMMENT = "#comment"; private static final String TARGET_NAMESPACE = "targetNamespace"; private static final String XML_SCHEMA_ATTR = "http://www.w3.org/2001/XMLSchema"; private static final String OPERATION = "operation"; private static final String OPERATION_1 = "wsdl:operation"; public ServiceDetail parse(String wsdlPath) throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { ServiceDetail sd = new ServiceDetail(wsdlPath); Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(readWsdl(wsdlPath)); removeComments(document, document.getChildNodes()); String tagPrefix = getNamespacePrefix(document); if (tagPrefix != null) { sd.setPrefix(tagPrefix); } String nameSpace = getTargetNamespace(document); sd.setNameSpace(nameSpace); List operations = getOperations(document); sd.setOperations(operations); String serviceName = getServiceName(document); sd.setServiceName(serviceName); String portName = getPortName(document); sd.setPortName(portName); return sd; } private String getServiceName(Document document) { String serviceName = null; if (document.getElementsByTagName(SERVICE).getLength() > 0) { NodeList nodeListOfService = document.getElementsByTagName(SERVICE); for (int i = 0; i 0) { NodeList nodeListOfService = document.getElementsByTagName(PORT); for (int i = 0; i < nodeListOfService.getLength(); i++) { Node portNode = nodeListOfService.item(i).getAttributes().getNamedItem("name"); portName = portNode.getNodeValue(); } } return portName; } private String getNamespacePrefix(Document document) { String tagPrefix = null; int l = document.getFirstChild().getAttributes().getLength(); for (int i = 0; i < l; i++) { String cmpAttribute = document.getFirstChild().getAttributes().item(i).getNodeValue(); if (cmpAttribute.equals(XML_SCHEMA_ATTR)) { tagPrefix = document.getFirstChild().getAttributes().item(i).getNodeName().replace("xmlns:", ""); } } return tagPrefix; } private String getTargetNamespace(Document document) { return document.getFirstChild().getAttributes().getNamedItem(TARGET_NAMESPACE).getNodeValue(); } private void removeComments(Document document, NodeList allNodesOfDocumnet) { for (int index = 0; index < allNodesOfDocumnet.getLength(); index++) { if (document.getFirstChild().getNodeName().equalsIgnoreCase(COMMENT)) { document.removeChild(document.getFirstChild()); } } } private List getOperations(Document document) { List operations = new ArrayList(); NodeList nodeListOfOperations = null; if ((document.getElementsByTagName(OPERATION).getLength() > 0) || (document.getElementsByTagName(OPERATION_1).getLength() > 0)) { if (document.getElementsByTagName(OPERATION).getLength() > 0) { nodeListOfOperations = document.getElementsByTagName(OPERATION); } else if (document.getElementsByTagName(OPERATION_1).getLength() > 0) { nodeListOfOperations = document.getElementsByTagName(OPERATION_1); } } for (int i = 0; i < nodeListOfOperations.getLength(); i++) { Operation ope = new Operation( nodeListOfOperations.item(i).getAttributes().getNamedItem("name").getNodeValue()); Node paraOrder = nodeListOfOperations.item(i).getAttributes().getNamedItem("parameterOrder"); if (paraOrder != null) { ope.setParameterNames(Arrays.asList(paraOrder.getNodeValue().split(" "))); } else { ope.setParameterNames(Arrays.asList("number".split(","))); } if (!operations.contains(ope)) { operations.add(ope); } } return operations; } private InputStream readWsdl(String wsdlUrl) throws IOException { URL url = new URL(wsdlUrl); URLConnection uc = url.openConnection(); return uc.getInputStream(); } }
Note: The parsing logic is based on Document
structure.
4.2.2 ParseWsdlServiceTest
Create ParseWsdlServiceTest
to test the parsing logic.
ParseWsdlServiceTest.java
package jcg.demo.jaxws.client; import static org.junit.Assert.assertEquals; import java.io.FileNotFoundException; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import org.junit.Test; import org.xml.sax.SAXException; import jcg.demo.jaxws.client.model.ServiceDetail; public class ParseWsdlServiceTest { private ParseWsdlService parser = new ParseWsdlService(); @Test public void parse_mathService() throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { String wsdlPath = "http://localhost:9990/mathServer?wsdl"; ServiceDetail mathService = parser.parse(wsdlPath); assertEquals("http://jcg.demo.mary/", mathService.getNameSpace()); assertEquals("MathServiceImplService", mathService.getServiceName()); assertEquals("MathServiceImplPort", mathService.getPortName()); assertEquals(2, mathService.getOperations().size()); assertEquals("sum", mathService.getOperations().get(0).getName()); assertEquals("isPrimeNumber", mathService.getOperations().get(1).getName()); } @Test public void parse_bookService() throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { String wsdlPath = "http://localhost:9980/bookServer?wsdl"; ServiceDetail bookService = parser.parse(wsdlPath); assertEquals("http://jcg.demo.zheng/", bookService.getNameSpace()); assertEquals("BookServiceImplService", bookService.getServiceName()); assertEquals("BookServiceImplPort", bookService.getPortName()); assertEquals(1, bookService.getOperations().size()); assertEquals("getBook", bookService.getOperations().get(0).getName()); } }
Note: The parsing logic is tested based on two clients only.
4.3. Build Dynamic Proxy Client
Construct Service
from ServiceDetail
and create invokeOperation
via Dispatch
inteface.
4.3.1. DynamicWebServiceClient
Create DynamicWebServiceClient
.
DynamicWebServiceClient.java
package jcg.demo.jaxws.client; import java.io.IOException; import java.util.List; import javax.xml.namespace.QName; import javax.xml.soap.MessageFactory; 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.SOAPMessage; import javax.xml.soap.SOAPPart; import javax.xml.ws.Dispatch; import javax.xml.ws.Service; import javax.xml.ws.soap.SOAPBinding; import jcg.demo.jaxws.client.model.Operation; import jcg.demo.jaxws.client.model.ServiceDetail; public class DynamicWebServiceClient { public SOAPMessage invokeOperation(ServiceDetail serviceDetail, String operationName, List args) throws SOAPException, IOException { QName serviceQN = new QName(serviceDetail.getNameSpace(), serviceDetail.getServiceName()); QName portQN = new QName(serviceDetail.getNameSpace(), serviceDetail.getPortName()); Service service = Service.create(serviceQN); service.addPort(portQN, SOAPBinding.SOAP11HTTP_BINDING, serviceDetail.getWsdl()); Operation foundOp = foundOperation(serviceDetail, operationName); if (foundOp == null) { throw new RuntimeException(serviceDetail.getWsdl() + " Not support operation: " + operationName); } Dispatch dispatcher = service.createDispatch(portQN, SOAPMessage.class, Service.Mode.MESSAGE); dispatcher.getRequestContext().put(Dispatch.USERNAME_PROPERTY, "mzheng"); dispatcher.getRequestContext().put(Dispatch.PASSWORD_PROPERTY, "great"); MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); SOAPMessage request = mf.createMessage(); SOAPPart part = request.getSOAPPart(); SOAPEnvelope env = part.getEnvelope(); env.addNamespaceDeclaration(serviceDetail.getPrefix(), serviceDetail.getNameSpace()); SOAPBody body = env.getBody(); SOAPElement operation = body.addChildElement(operationName, serviceDetail.getPrefix()); int argrIndex = 0; for (String arg : foundOp.getParameterNames()) { SOAPElement value = operation.addChildElement(arg); value.addTextNode(args.get(argrIndex)); argrIndex++; } request.saveChanges(); System.out.println("Request: " + SoapMessageUtil.outputSoapMessage(request)); return dispatcher.invoke(request); } private Operation foundOperation(ServiceDetail serviceDetail, String operationName) { Operation operation = serviceDetail.getOperations().stream().filter(e -> e.getName().equals(operationName)) .findFirst().orElse(null); return operation != null ? operation : null; } }
4.3.2. DynamicWebServiceClientTest
Create DynamicWebServiceClientTest
to test
DynamicWebServiceClientTest.java
package jcg.demo.jaxws.client; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.xml.soap.SOAPMessage; import org.junit.Test; import jcg.demo.jaxws.client.model.Operation; import jcg.demo.jaxws.client.model.ServiceDetail; public class DynamicWebServiceClientTest { private DynamicWebServiceClient webServiceClient = new DynamicWebServiceClient(); @Test public void invoke_mathService_isPrimeNumber_happyPath() { ServiceDetail wsdlInfor = setMathServiceDetail(); try { SOAPMessage response = webServiceClient.invokeOperation(wsdlInfor, "isPrimeNumber", Arrays.asList("5".split(","))); assertNotNull(response); String ret = SoapMessageUtil.outputSoapMessage(response); assertTrue(ret.contains("true")); } catch (Exception e) { System.out.println("Error " + e.getMessage()); } } @Test public void invoke_mathService_sum_happyPath() { ServiceDetail wsdlInfor = setMathServiceDetail(); try { SOAPMessage response = webServiceClient.invokeOperation(wsdlInfor, "sum", Arrays.asList("4,5".split(","))); assertNotNull(response); String ret = SoapMessageUtil.outputSoapMessage(response); assertTrue(ret.contains("9")); } catch (Exception e) { System.out.println("Error " + e.getMessage()); } } private ServiceDetail setMathServiceDetail() { ServiceDetail wsdlInfor = new ServiceDetail("http://localhost:9990/mathServer?wsdl"); wsdlInfor.setNameSpace("http://jcg.demo.mary/"); List operations = new ArrayList(); operations.add(new Operation("isPrimeNumber", Arrays.asList("number".split(",")))); operations.add(new Operation("sum", Arrays.asList("int_a,int_b".split(",")))); wsdlInfor.setOperations(operations); wsdlInfor.setPortName("MathServiceImplPort"); wsdlInfor.setPrefix("jcg"); wsdlInfor.setServiceName("MathServiceImplService"); return wsdlInfor; } }
4.4. Create Dynamic Web Service Application
Create a dynamic web service application by parsing the web service WSDL and then build the service with the service detail.
ClientApp.java
package jcg.demo.jaxws.client; import java.io.FileNotFoundException; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import javax.xml.parsers.ParserConfigurationException; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; import org.xml.sax.SAXException; import jcg.demo.jaxws.client.model.ServiceDetail; public class ClientApp { public static void main(String[] args) { DynamicWebServiceClient dynamicClient = new DynamicWebServiceClient(); ParseWsdlService parseService = new ParseWsdlService(); try { mathClient_sum(dynamicClient, parseService); mathClient_isPrimeNumber(dynamicClient, parseService); bookClient(dynamicClient, parseService); } catch (SOAPException | SAXException | IOException | ParserConfigurationException e) { e.printStackTrace(); } } private static void mathClient_sum(DynamicWebServiceClient dynamicClient, ParseWsdlService parseService) throws FileNotFoundException, SAXException, IOException, ParserConfigurationException, SOAPException { ServiceDetail mathService = parseService.parse("http://localhost:9990/mathServer?wsdl"); Instant start = Instant.now(); SOAPMessage response = dynamicClient.invokeOperation(mathService, "sum", Arrays.asList("4,5".split(","))); Instant end = Instant.now(); System.out.println("Response: " + SoapMessageUtil.outputSoapMessage(response)); System.out.println("math_dynamicClient_sum, took " + Duration.between(start, end)); } private static void mathClient_isPrimeNumber(DynamicWebServiceClient dynamicClient, ParseWsdlService parseService) throws FileNotFoundException, SAXException, IOException, ParserConfigurationException, SOAPException { ServiceDetail mathService = parseService.parse("http://localhost:9990/mathServer?wsdl"); Instant start = Instant.now(); SOAPMessage response = dynamicClient.invokeOperation(mathService, "isPrimeNumber", Arrays.asList("45".split(","))); Instant end = Instant.now(); System.out.println("Response: " + SoapMessageUtil.outputSoapMessage(response)); System.out.println("math_dynamicClient_isPrimeNumber, took " + Duration.between(start, end)); } private static void bookClient(DynamicWebServiceClient dynamicClient, ParseWsdlService parseService) throws FileNotFoundException, SAXException, IOException, ParserConfigurationException, SOAPException { ServiceDetail service = parseService.parse("http://localhost:9980/bookServer?wsdl"); for (int i = 1; i < 6; i++) { Instant start = Instant.now(); SOAPMessage response = dynamicClient.invokeOperation(service, "getBook", Arrays.asList(String.valueOf(i).split(","))); Instant end = Instant.now(); System.out.println("Response: " + SoapMessageUtil.outputSoapMessage(response)); System.out.println("book_dynamicClient_getBook, took " + Duration.between(start, end)); } } }
Image shows both jax-ws-client-dynamic
and jax-ws-client-static
structure.
5. Demo Time
Start ServerApp
which includes both MathService
and BookService
.
Start BookServiceClient
from jax-ws-client-static
and capture the output:
BookServiceClient output
Mary Book book_staticClient_getBook, took PT0.036S Terry Book book_staticClient_getBook, took PT0.006S Ben Best Book book_staticClient_getBook, took PT0.005S Alex Life book_staticClient_getBook, took PT0.005S David Music book_staticClient_getBook, took PT0.004S
Start MathServiceClient
from jax-ws-client-static
and capture the output:
MathServiceClient output
100003 is prime number. math_staticClient_isPrimeNumber, took PT0.004S 100019 is prime number. math_staticClient_isPrimeNumber, took PT0.004S 100043 is prime number. math_staticClient_isPrimeNumber, took PT0.005S 100049 is prime number. math_staticClient_isPrimeNumber, took PT0.004S 100057 is prime number. math_staticClient_isPrimeNumber, took PT0.004S 100069 is prime number. math_staticClient_isPrimeNumber, took PT0.004S
Start ClientApp
from jax-ws-client-dynamic
and capture the output:
ClientApp output
Request: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://jcg.demo.mary/"><SOAP-ENV:Header/><SOAP-ENV:Body><xsd:sum><int_a>4</int_a><int_b>5</int_b></xsd:sum></SOAP-ENV:Body></SOAP-ENV:Envelope> Response: <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:sumResponse xmlns:ns2="http://jcg.demo.mary/"><return>9</return></ns2:sumResponse></S:Body></S:Envelope> math_dynamicClient_sum, took PT0.484S Request: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://jcg.demo.mary/"><SOAP-ENV:Header/><SOAP-ENV:Body><xsd:isPrimeNumber><number>45</number></xsd:isPrimeNumber></SOAP-ENV:Body></SOAP-ENV:Envelope> Response: <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:isPrimeNumberResponse xmlns:ns2="http://jcg.demo.mary/"><return>false</return></ns2:isPrimeNumberResponse></S:Body></S:Envelope> math_dynamicClient_isPrimeNumber, took PT0.05S Request: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://jcg.demo.zheng/"><SOAP-ENV:Header/><SOAP-ENV:Body><xsd:getBook><number>1</number></xsd:getBook></SOAP-ENV:Body></SOAP-ENV:Envelope> Response: <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:getBookResponse xmlns:ns2="http://jcg.demo.zheng/"><return><id>1</id><name>Mary Book</name></return></ns2:getBookResponse></S:Body></S:Envelope> book_dynamicClient_getBook, took PT0.057S Request: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://jcg.demo.zheng/"><SOAP-ENV:Header/><SOAP-ENV:Body><xsd:getBook><number>2</number></xsd:getBook></SOAP-ENV:Body></SOAP-ENV:Envelope> Response: <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:getBookResponse xmlns:ns2="http://jcg.demo.zheng/"><return><id>2</id><name>Terry Book </name></return></ns2:getBookResponse></S:Body></S:Envelope> book_dynamicClient_getBook, took PT0.055S Request: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://jcg.demo.zheng/"><SOAP-ENV:Header/><SOAP-ENV:Body><xsd:getBook><number>3</number></xsd:getBook></SOAP-ENV:Body></SOAP-ENV:Envelope> Response: <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:getBookResponse xmlns:ns2="http://jcg.demo.zheng/"><return><id>3</id><name>Ben Best Book</name></return></ns2:getBookResponse></S:Body></S:Envelope> book_dynamicClient_getBook, took PT0.036S Request: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://jcg.demo.zheng/"><SOAP-ENV:Header/><SOAP-ENV:Body><xsd:getBook><number>4</number></xsd:getBook></SOAP-ENV:Body></SOAP-ENV:Envelope> Response: <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:getBookResponse xmlns:ns2="http://jcg.demo.zheng/"><return><id>4</id><name>Alex Life</name></return></ns2:getBookResponse></S:Body></S:Envelope> book_dynamicClient_getBook, took PT0.034S Request: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://jcg.demo.zheng/"><SOAP-ENV:Header/><SOAP-ENV:Body><xsd:getBook><number>5</number></xsd:getBook></SOAP-ENV:Body></SOAP-ENV:Envelope> Response: <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:getBookResponse xmlns:ns2="http://jcg.demo.zheng/"><return><id>5</id><name>David Music</name></return></ns2:getBookResponse></S:Body></S:Envelope> book_dynamicClient_getBook, took PT0.031S
Note: Static web service client is 25ms
faster than the dynamic web service client for the same operation.
5.1 Update JAX-WS Server and Demo Again
Stop ServerApp
and change the MathService
interface and then repeat the steps above.
The MathServiceClient
caught exception because the generated stub is out of sync with service implementation, but the dynamic web service client ClientApp
still works as expected.
6. Summary
In this example, I built two simple JAX-WS services and static and dynamic web service client with JDK libraries only. Other vendors, such as Apache CXF, provides JAxWsProxyFactoryBean
to achieve the same goal.
The dynamic web service client is a little slower than the static web service, but provides greater flexibility.
Click here for the JAX-WS client solutions comparison.
7. Download the Source Code
This example consists of a JAX-WS server, a static web service client and a dynamic web service client.
You can download the full source code of this example here:JAX-WS Dynamic Proxy Client Example
A couple details:
1. You forgot to show the SoapMessageUtil.java code here, however it’s included inside the JAX-WS-Demo.zip linked at the bottom of the article.
2. When copying from your IDE to web, parameterized types between > and < symbols, are not shown, for those copying from web to IDE instead of using the pack.
Cheers!
I tried executing the code but I am getting SOAP Fault – com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client received SOAP Fault from server: Cannot find dispatch method for {http://impl.service.jaxws.demo.jcg/}sum Please see the server log to find more detail regarding exact cause of the failure.
What can be the reason for this? I am not able to run the code. Please solve.