With just 4 files we have a full Maven project that can generate a JAX-WS service client and do an automated integration test. The project is available on GitHub and is linked in the last section. This post just adds a bit of prose on how it works.
First let's introduce the WSDL file that the service client is generated from.
The WSDL file in the GitHub project describes a Document/Literal Wrapped service. These three words refer to how we use the SOAP body.
Document style means the content of the SOAP body can model any kind of data that can be expressed with XML as opposed to RPC style which would model a remote procedure call with a procedure name and a number of parameters.
Literal use means the XML in the SOAP body is constrained by a regular XML Schema as opposed to being restricted by special encoding rules for procedure call parameters.
Wrapped is not a standard, just a convention that the SOAP body may only contain one child, a so-called wrapper element. This one-child body is also a requirement for WS-I compliance. For requests the wrapper element is named after the operation and for responses it's the name of the operation with the word "Response" appended.
Let's take a look at a sample service request that calls the operation "SendMessage".
Notice that the HTTP header and the SOAP header contain no mention of the service operation. Only the SOAP body wrapper element is named after the operation. Knowing the operation is vital to the service consumer, because it will use the information to dispatch an incoming request to the subsystem that processes that type of operation. There are alternatives, for instance with WS-Addressing we could add an Action element to the SOAP header that is associated with the operation, but the Wrapped convention is the default for JAX-WS services.
And here is the response from the SendMessage operation. The SOAP body has one child named "SendMessageResponse":
The WSDL file is stored in the Maven project under src/main/resources/test.wsdl and its content is shown below.
One way to understand a WSDL file is to view it as a description of an abstract interface and a binding to a concrete implementation. Sort of like an interface in Java and its implementation class.
The abstract interface is the service, porttype and types elements. They describe the messages that model business concepts and the operations that express the type of processing we want.
The binding element is the implementation. It deals with such things as how to transport, route and secure the messages by binding the port and its operations to the HTTP and SOAP protocol.
The WSDL file presented above is a little too simple to see all the types of artifacts that can be created for a JAX-WS service client, but there is enough to make the following point: The artifacts are mostly Java representations of the abstract interface described in the WSDL and when we use the service client, JAX-WS does a decent job to hide the actual implementation of HTTP and SOAP. From a code point of view it's like we are just invoking a method on a regular Java object. Behind that facade of simplicity there's a network connection with quite a bit of complexity.
This code snippet shows how to create an instance of the generated Service and PortType classes and invoke the sendMessage() operation.
As we shall see later in the JUnit testcase, some details of the implementation become visible in the code, when we need to override the service endpoint network address. It is possible to hide such configuration in a SoapHandler, but I will defer that for another post.
Wsimport is a commandline tool bundled with Oracle's JDK. It is used to generate the JAX-WS artifacts for the service client. We will use it indirectly through the "wsimport" goal of the Maven plugin "jaxws-maven-plugin". The "wsimport" goal creates the artifacts in Maven's "generate-sources" phase.
The following snippet shows a minimal configuration of the jaxws-maven-plugin in the projects pom.xml file:
wsdlDirectory points to the folder of the WSDL file that is used by wsimport to create the JAX-WS artifacts.
wsdlLocation tells wsimport to create a Service class that will load the WSDL from the classpath "/test.wsdl" at runtime, rather than the default which is to fetch it from the network address listed in the WSDL file in the Service element. Because the test.wsdl file is stored under /src/main/resources, it is automatically packaged with the Jar file created by Maven.
extension enables the vendor extension to support SOAP version 1.2.
The JUnit integration test calls the SendMessage operation using the JAX-WS service client and asserts the response is as expected. The file is stored in the Maven project under src/test/java/com/sorenpoulsen/serviceclient/ServiceTest.java.
The service client directs its call to a SoapUI mock service, that is launched from the Maven project just before Maven's test phase. The SoapUI project XML file is stored under src/test/soapui/JaxwsServiceClientTest-soapui-project.xml. I wont go through the details of how to use SoapUI to create mock services or how to use the soapui-maven-plugin, there's another post about the topic here: mock-services-for-maven-integration-tests-with-the-soapui-plugin.
The Maven project is available from GitHub at https://github.com/SorenPoulsen/jaxws-service-client-with-maven.
Hit the terminal and run these commands to clone, build and run the Maven project:
$ git clone https://github.com/SorenPoulsen/jaxws-service-client-with-maven.git $ cd jaxws-service-client-with-maven $ mvn test