Learn how to create SOAP-based Web services JAX-WS supports SOAP-based Web services. Part 2 of this four-part series on Java SE Web services defines a SOAP-based units-conversion Web service, builds and then verifies this Web service locally via the default lightweight HTTP server (discussed in Part 1), interprets the service’s WSDL document, and accesses the service from a simple client. Defining a units-conversion web service The units-conversion Web service, which I’ve named UC, consists of four functions for converting between centimeters and inches and between degrees Fahrenheit and degrees Celsius. Although this example could be architected as a single Java class, I’ve chosen to follow best practices by architecting it as a Java interface and a Java class. Listing 1 presents the Web service’s UC interface. Listing 1. The UC Web service’s Service Endpoint Interface package ca.javajeff.uc; import javax.jws.WebMethod; import javax.jws.WebService; @WebService public interface UC { @WebMethod double c2f(double degrees); @WebMethod double cm2in(double cm); @WebMethod double f2c(double degrees); @WebMethod double in2cm(double in); } UC describes a Service Endpoint Interface (SEI), which is a Java interface that exposes a Web service interface’s operations in terms of abstract Java methods. Clients communicate with SOAP-based Web services via their SEIs. UC is declared to be an SEI via the @WebService annotation. When a Java interface or class is annotated @WebService, all public methods whose parameters, return values, and declared exceptions follow the rules defined in Section 5 of the JAX-RPC 1.1 specification describe Web service operations. Because only public methods can be declared in interfaces, the public reserved word isn’t necessary when declaring c2f(), cm2in(), f2c(), and in2cm(). These methods are implicitly public. Each method is also annotated @WebMethod. Although @WebMethod isn’t essential in this example, its presence reinforces the fact that the annotated method exposes a Web service operation. Listing 2 presents the Web service’s UCImpl class. Listing 2. The UC Web service’s Service Implementation Bean package ca.javajeff.uc; import javax.jws.WebService; @WebService(endpointInterface = "ca.javajeff.uc.UC") public class UCImpl implements UC { @Override public double c2f(double degrees) { return degrees * 9.0 / 5.0 + 32; } @Override public double cm2in(double cm) { return cm / 2.54; } @Override public double f2c(double degrees) { return (degrees - 32) * 5.0 / 9.0; } @Override public double in2cm(double in) { return in * 2.54; } } UCImpl describes a Service Implementation Bean (SIB), which provides an implementation of the SEI. This class is declared to be a SIB via the @WebService(endpointInterface = "ca.javajeff.uc.UC") annotation. The endpointInterface element connects this SIB to its SEI, and is necessary to avoid undefined port type errors when running the client application presented later. The implements UC clause isn’t absolutely necessary. If this clause isn’t present, the UC interface is ignored (and is redundant). However, it’s a good idea to keep implements UC so the compiler can verify that the SEI’s methods have been implemented in the SIB. The SIB’s method headers aren’t annotated @WebMethod because this annotation is typically used in the context of the SEI. However, if you were to add a public method (which conforms to the rules in Section 5 of the JAX-RPC 1.1 specification) to the SIB, and if this method doesn’t expose a Web service operation, you would annotate the method header @WebMethod(exclude = true). By assigning true to @WebMethod‘s exclude element, you prevent that method from being associated with an operation. This Web service is ready to be published so that it can be accessed from clients. Listing 3 presents a UCPublisher application that accomplishes this task in the context of the default lightweight HTTP server. Listing 3. Publishing UC import javax.xml.ws.Endpoint; import ca.javajeff.uc.UCImpl; public class UCPublisher { public static void main(String[] args) { Endpoint.publish("http://localhost:9901/UC", new UCImpl()); } } Publishing the Web service involves making a single call to the EndPoint class’s Endpoint publish(String address, Object implementor) class method. The address parameter identifies the URI assigned to the Web service. I’ve chosen to publish this Web service on the local host by specifying localhost (equivalent to IP address 127.0.0.1) and port number 9901 (which is most likely available). Also, I’ve arbitrarily choosen /UC as the publication path. The implementor parameter identifies an instance of UC‘s SIB. The publish() method creates and publishes an endpoint for the specified implementor object at the given address, and uses the implementor‘s annotations to create Web Services Definition Language (WSDL) and XML Schema documents. It causes the necessary server infrastructure to be created and configured by the JAX-WS implementation based on some default configuration. Furthermore, this method causes the application to run indefinitely. (On Windows machines, press the Ctrl and C keys simultaneously to terminate the application.) Building and verifying the web service It’s not difficult to build the previously-defined UC Web service. First, you need to create a suitable directory structure containing the appropriate files. Accomplish this task by performing the following steps: Within the current directory, create a ca directory. Within ca, create a javajeff directory. Finally, within javajeff, create a uc directory. Copy Listing 1 to a UC.java source file and store this file in ca/javajeff/uc. Copy Listing 2 to a UCImpl.java source file and store this file in ca/javajeff/uc. Copy Listing 3 to a UCPublisher.java source file and store this file in the current directory, which contains the ca directory. The next task is to compile these source files. Assuming that you haven’t changed directories, execute the following command to compile these source files in Java SE 9 (omit --add-modules java.xml.ws in Java SE 6, 7, or 8): javac --add-modules java.xml.ws UCPublisher.java If these source files compile successfully, execute the following command to run this application in Java 9 (omit --add-modules java.xml.ws in Java SE 6, 7, or 8): java --add-modules java.xml.ws UCPublisher While the application runs, use a Web browser to verify that this Web service is running correctly and to access its WSDL document. Start your favorite Web browser and enter the following line in the address bar: http://localhost:9901/UC Figure 1 shows the resulting Web page in the Google Chrome Web browser. Figure 1. UC’s Web page provides detailed information on the published Web service Figure 1 presents the Web service endpoint’s qualified service and port names. (Notice that the package name has been inverted — uc.javajeff.ca instead of ca.javajeff.uc). A client uses these names to access the service. Figure 1 also presents the address URI of the Web service, the location of the Web service’s WSDL document (the Web service URI suffixed by the ?wsdl query string), and the package-qualified name of the Web service implementation class. Interpreting the web service’s WSDL document The location of the UC Web service’s WSDL document is presented as a link. Click this link to view the WSDL document, whose contents are presented in Listing 4. Listing 4. UC’s WSDL document <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://uc.javajeff.ca/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://uc.javajeff.ca/" name="UCImplService"> <types> <xsd:schema> <xsd:import namespace="http://uc.javajeff.ca/" schemaLocation="http://localhost:9901/UC?xsd=1"/> </xsd:schema> </types> <message name="c2f"> <part name="parameters" element="tns:c2f"/> </message> <message name="c2fResponse"> <part name="parameters" element="tns:c2fResponse"/> </message> <message name="in2cm"> <part name="parameters" element="tns:in2cm"/> </message> <message name="in2cmResponse"> <part name="parameters" element="tns:in2cmResponse"/> </message> <message name="cm2in"> <part name="parameters" element="tns:cm2in"/> </message> <message name="cm2inResponse"> <part name="parameters" element="tns:cm2inResponse"/> </message> <message name="f2c"> <part name="parameters" element="tns:f2c"/> </message> <message name="f2cResponse"> <part name="parameters" element="tns:f2cResponse"/> </message> <portType name="UC"> <operation name="c2f"> <input wsam:Action="http://uc.javajeff.ca/UC/c2fRequest" message="tns:c2f"/> <output wsam:Action="http://uc.javajeff.ca/UC/c2fResponse" message="tns:c2fResponse"/> </operation> <operation name="in2cm"> <input wsam:Action="http://uc.javajeff.ca/UC/in2cmRequest" message="tns:in2cm"/> <output wsam:Action="http://uc.javajeff.ca/UC/in2cmResponse" message="tns:in2cmResponse"/> </operation> <operation name="cm2in"> <input wsam:Action="http://uc.javajeff.ca/UC/cm2inRequest" message="tns:cm2in"/> <output wsam:Action="http://uc.javajeff.ca/UC/cm2inResponse" message="tns:cm2inResponse"/> </operation> <operation name="f2c"> <input wsam:Action="http://uc.javajeff.ca/UC/f2cRequest" message="tns:f2c"/> <output wsam:Action="http://uc.javajeff.ca/UC/f2cResponse" message="tns:f2cResponse"/> </operation> </portType> <binding name="UCImplPortBinding" type="tns:UC"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="c2f"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="in2cm"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="cm2in"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="f2c"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="UCImplService"> <port name="UCImplPort" binding="tns:UCImplPortBinding"> <soap:address location="http://localhost:9901/UC"/> </port> </service> </definitions> A WSDL document is an XML document with a definitions root element, which makes a WSDL document nothing more than a set of definitions. This element includes various xmlns attributes for identifying various standard namespaces, along with targetNameSpace and name attributes: The targetNamespace attribute creates a namespace for all user-defined elements in the WSDL document (such as the c2f element defined via the message element with this name). This namespace is used to distinguish between the user-defined elements of the current WSDL document and user-defined elements of imported WSDL documents, which are identified via WSDL’s import element. In a similar fashion, the targetNamespace attribute that appears on an XML Schema-based file’s schema element creates a namespace for its user-defined simple type elements, attribute elements, and complex type elements. The name attribute identifies the Web service and is used only to document the service. Nested within definitions are types, message, portType, binding, and service elements: types presents user-defined data types (used in the context of message elements) under a data type system. Although any type definition language can be used, XML Schema is mandated by the WS-I in Basic Profile 1.0. The types element can contain zero or more schema elements; this example has a single schema element, which imports an external schema. Furthermore, types is optional. It’s not present when the service uses only XML Schema builtin simple types, such as strings and integers. message defines a one-way request or response message (conceptually a function invocation or an invocation response) that may consist of one or more parts (conceptually equivalent to function parameters or return values). Each part is described by a part element whose name attribute identifies a parameter/return value element. The element attribute identifies another element (defined elsewhere) whose value is passed to this parameter or which provides the response value. Zero or more part elements, and zero or more message elements may be specified. portType describes a Web service interface via its operations. Each operation element contains input and/or output elements based on the Message-Exchange Pattern. Listing 4 includes both elements. (A fault element for communicating error information can be specified when there is an output element.) The wsam:Action attribute is used with message routing in the context of WS-Addressing. The message attribute identifies the message element that describes the message via its name attribute (and also provides the part elements describing parameters and return value). operation elements are optional; at least one portType element must be specified. binding provides details on how a portType operation (such as c2f or f2c) is transmitted over the wire. This element’s type attribute identifies the portType element defined earlier in the document. The nested soap:binding element indicates that a SOAP 1.1 binding is being used. Its transport attribute’s URI value identifies HTTP as the transport protocol (SOAP over HTTP), and its style attribute identifies document as the default service style. Each operation element consists of soap:operation, input, and output elements. The soap:operation element is a SOAP extension element that provides extra binding information at the operation level. Servers (such as firewalls) can use the SOAPAction attribute’s URI value (when present) to filter SOAP request messages sent via HTTP. The input and output elements contain soap:body elements whose use attributes indicate how message parts appear inside of SOAP’s Body element –- I present an overview of SOAP later in this series. The literal value means that these parts appear literally instead of being encoded. Multiple binding elements can be specified. service defines a collection of endpoints in terms of nested port elements that expose bindings –- a port element’s binding attribute identifies a binding element. Furthermore, the port element identifies the service’s address; because we are dealing with a SOAP service, port contains a soap:address element whose location attribute specifies this address. The types, message, and portType elements are abstract definitions of the Web service’s interface. They form the interface between the Web service and an application. The binding and service elements provide concrete details on how this interface is mapped to messages transmitted over the wire. JAX-WS handles these details on behalf of the application. Understanding style and use The soap:binding element’s style attribute affects how a SOAP message’s Body element is built by indicating whether the operation is document-oriented (messages contain documents) — the value is document — or Remote Procedure Call (RPC)-oriented (messages contain parameters and return values) — the value is rpc. I discuss SOAP message architecture later in this series. The soap:body element’s use attribute indicates whether the WSDL document’s message element’s part child elements define the concrete schema of the message — the value is literal — or are encoded via certain encoding rules — the value is encoded. When use is set to literal, each part element references a concrete schema definition using either the element or type attribute. For element, the referenced element will appear directly under the SOAP message’s Body element (for document style bindings) or under an accessor element named after the message part (for rpc style bindings). For type, the referenced type becomes the schema type of the enclosing element (Body for document style or part accessor element for rpc style). When use is set to encoded, each part element references an abstract type using the type attribute. These abstract types are used to produce a concrete message by applying an encoding specified by the SOAP message’s encodingStyle attribute. To learn more about the style and use attributes, check out Which style of WSDL should I use?. Exploring the XML schema document The types element’s schema element identifies the location of the schema where each operation’s return and parameter types are described. The xsd:import tag’s schemaLocation attribute identifies this location as http://localhost:9901/UC?xsd=1. When you point your Google Chrome browser to this location, you observe Listing 5 (Mozilla Firefox hides the xmlns attributes). Listing 5. The WSDL document’s referenced XML Schema document <xs:schema xmlns:tns="http://uc.javajeff.ca/" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://uc.javajeff.ca/"> <xs:element name="c2f" type="tns:c2f"/> <xs:element name="c2fResponse" type="tns:c2fResponse"/> <xs:element name="cm2in" type="tns:cm2in"/> <xs:element name="cm2inResponse" type="tns:cm2inResponse"/> <xs:element name="f2c" type="tns:f2c"/> <xs:element name="f2cResponse" type="tns:f2cResponse"/> <xs:element name="in2cm" type="tns:in2cm"/> <xs:element name="in2cmResponse" type="tns:in2cmResponse"/> <xs:complexType name="cm2in"> <xs:sequence> <xs:element name="arg0" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="cm2inResponse"> <xs:sequence> <xs:element name="return" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="in2cm"> <xs:sequence> <xs:element name="arg0" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="in2cmResponse"> <xs:sequence> <xs:element name="return" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="c2f"> <xs:sequence> <xs:element name="arg0" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="c2fResponse"> <xs:sequence> <xs:element name="return" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="f2c"> <xs:sequence> <xs:element name="arg0" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="f2cResponse"> <xs:sequence> <xs:element name="return" type="xs:double"/> </xs:sequence> </xs:complexType> </xs:schema> For brevity, I won’t delve into Listing 5. Instead, I refer you to Chapter 1 in my Java XML and JSON book (see the advertisement at the end of this post). This chapter explores XML Schema and makes sense of the various elements in the listing. Accessing the web service from a simple client I’ve created a simple UCClient Java application that demonstrates the UC Web service. Listing 6 presents this application’s source code. Listing 6. A client for accessing the UC Web service import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import ca.javajeff.uc.UC; public class UCClient { public static void main(String[] args) throws Exception { URL url = new URL("http://localhost:9901/UC?wsdl"); QName qname = new QName("http://uc.javajeff.ca/", "UCImplService"); Service service = Service.create(url, qname); qname = new QName("http://uc.javajeff.ca/", "UCImplPort"); UC uc = service.getPort(qname, UC.class); // UC uc = service.getPort(UC.class); System.out.printf("DC to DF: 37 DC = %f DF%n", uc.c2f(37.0)); System.out.printf("CM to IN: 10 CM = %f IN%n", uc.cm2in(10)); System.out.printf("DF to DC: 212 DF = %f DC%n", uc.f2c(212.0)); System.out.printf("IN to CM: 10 IN = %f CM%n", uc.in2cm(10)); } } UCClient first creates a java.net.URL instance that identifies the Web service’s WSDL file. It then creates a javax.xml.namespace.QName instance that identifies the endpoint’s qualified service name (see Figure 1). These instances are passed to the javax.xml.ws.Service class’s Service create(URL wsdlDocumentLocation, QName serviceName) class method to return a Service instance that provides a client view of a Web service. Service‘s T getPort(QName portName, Class<T> serviceEndpointInterface) method is then called on the Service instance to return a proxy for communicating with the Web service via its endpoint. The qualified name passed to portName identifies the endpoint’s qualified port name (see Figure 1), which identifies the Web service interface whose operations are to be accessed — there’s only one interface in this example. The java.lang.Class instance passed to serviceEndpointInterface identifies the UC SEI. This method returns a proxy object whose class implements UC, or throws javax.xml.ws.WebServiceException when something goes wrong (such as when not specifying endpointInterface in the UCImpl SIB’s @WebService annotation, and calling Service‘s T getPort(Class<T> serviceEndpointInterface) method, which uses endpointinterface to access the SEI). Assuming that getPort() succeeds, the returned object is used to invoke the c2f(), cm2in(), f2c(), and in2cm() methods with arguments representing body temperature in degrees Celsius, a specific number of centimeters, the boiling point of water in degrees Fahrenheit, and a specfic number of inches, respectively. Building and running the web service client Assuming that the current directory contains UCClient.java and that the parent directory includes a uc directory containing UC’s files, compile Listing 6 in Java 9 via the following command: javac -cp ..uc --add-modules java.xml.ws UCClient.java Assuming successful compilation, execute the following command to run the client against the UC Web service, which must be running: java -cp ..uc;. --add-modules java.xml.ws UCClient You should observe the following output: DC to DF: 37 DC = 98.600000 DF CM to IN: 10 CM = 3.937008 IN DF to DC: 212 DF = 100.000000 DC IN to CM: 10 IN = 25.400000 CM Working with wsimport Because the WSDL document in Listing 4 contains enough information to let clients communicate with the Web service, you can alternatively use the wsimport tool to generate client-support code from this document, to facilitate creating the client. In the context of UC, you would use this tool as follows: wsimport -keep –p client http://localhost:9901/UC?wsdl wsimport outputs parsing WSDL..., Generating code..., and Compiling code... messages; and generates the class files that a client needs to access this Web service. The -keep option causes wsimport to save the source code for these class files as well, which helps us learn how clients access the Web service, and makes it possible to add client-side handlers for intercepting messages (discussed later in this series). The -p option identifies the package directory in which to store the generated source and/or class files. You can specify any meaningful name (such as client) and wsimport will create a package directory with this name, and store the package directory structure underneath. Along with class files, wsimport stores UC.java, UCImplService.java, and other source files in the client directory. The former source file’s Java interface declares the same methods as Listing 1’s UC SEI interface, but with c2F, cm2In, f2C, and in2Cm method names replacing c2f, cm2in, f2c, and in2cm to adhere to a JAXB naming convention where the first letter of each subsequent word in a method name is capitalized. The latter file’s class, which is presented in Listing 7, provides a noargument constructor for instantiating this class, and a getUCImplPort() method that returns an instance of the generated UC interface; the client executes the Web service’s operations on this instance. Listing 7. A cleaned up service implementation class for accessing the UC Web service package client; import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import javax.xml.ws.WebEndpoint; import javax.xml.ws.WebServiceClient; import javax.xml.ws.WebServiceException; import javax.xml.ws.WebServiceFeature; /** * This class was generated by the JAX-WS RI. * JAX-WS RI 2.3.0-SNAPSHOT * Generated source version: 2.2 * */ @WebServiceClient(name = "UCImplService", targetNamespace = "http://uc.javajeff.ca/", wsdlLocation = "http://localhost:9901/UC?wsdl") public class UCImplService extends Service { private final static URL UCIMPLSERVICE_WSDL_LOCATION; private final static WebServiceException UCIMPLSERVICE_EXCEPTION; private final static QName UCIMPLSERVICE_QNAME = new QName("http://uc.javajeff.ca/", "UCImplService"); static { URL url = null; WebServiceException e = null; try { url = new URL("http://localhost:9901/UC?wsdl"); } catch (MalformedURLException ex) { e = new WebServiceException(ex); } UCIMPLSERVICE_WSDL_LOCATION = url; UCIMPLSERVICE_EXCEPTION = e; } public UCImplService() { super(__getWsdlLocation(), UCIMPLSERVICE_QNAME); } public UCImplService(WebServiceFeature... features) { super(__getWsdlLocation(), UCIMPLSERVICE_QNAME, features); } public UCImplService(URL wsdlLocation) { super(wsdlLocation, UCIMPLSERVICE_QNAME); } public UCImplService(URL wsdlLocation, WebServiceFeature... features) { super(wsdlLocation, UCIMPLSERVICE_QNAME, features); } public UCImplService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } public UCImplService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) { super(wsdlLocation, serviceName, features); } /** * @return * returns UC */ @WebEndpoint(name = "UCImplPort") public UC getUCImplPort() { return super.getPort(new QName("http://uc.javajeff.ca/", "UCImplPort"), UC.class); } /** * @param features * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the * proxy. Supported features not in the <code>features parameter * will have their default values. * @return * returns UC */ @WebEndpoint(name = "UCImplPort") public UC getUCImplPort(WebServiceFeature... features) { return super.getPort(new QName("http://uc.javajeff.ca/", "UCImplPort"), UC.class, features); } private static URL __getWsdlLocation() { if (UCIMPLSERVICE_EXCEPTION!= null) { throw UCIMPLSERVICE_EXCEPTION; } return UCIMPLSERVICE_WSDL_LOCATION; } } UCImplService extends the Service class to provide the client view of a Web service. There are two items to note: The noargument constructor is equivalent to Listing 6’s Service.create() method call. getUCImplPort() is equivalent to Listing 6’s getPort() method call. Listing 8 presents the source code to a UCClient2 class that demonstrates how a client can use UC and UCImplService to access the Web service. Listing 8. A simplified client for accessing the UC Web service import client.UC; import client.UCImplService; public class UCClient2 { public static void main(String[] args) throws Exception { UCImplService ucis = new UCImplService(); UC uc = ucis.getUCImplPort(); System.out.printf("DC to DF: 37 DC = %f DF%n", uc.c2F(37.0)); System.out.printf("CM to IN: 10 CM = %f IN%n", uc.cm2In(10)); System.out.printf("DF to DC: 212 DF = %f DC%n", uc.f2C(212.0)); System.out.printf("IN to CM: 10 IN = %f CM%n", uc.in2Cm(10)); } } Compile Listing 8 and run the resulting application as you did with UCClient — just make sure to replace each UCClient instance with UCClient2. You should observe the same output as shown earlier. Conclusion JAX-WS also supports RESTful Web services, which many developers find easier to work with than SOAP-based Web services. Part 3 of this series shows you how to build your own RESTful Web services in Java SE. download Download the source Get the source code for this post’s applications. Created by Jeff Friesen for JavaWorld The following software was used to develop the post’s code: 64-bit JDK 9ea+181 The post’s code was tested on the following platform(s): JVM on 64-bit Windows 8.1 Java