Developing a web service client

As we mentioned earlier, executable code needs to be generated from a web service's WSDL. A web service client will then invoke this executable code to access the web service.

The Java Development Kit (JDK) includes a utility to generate Java code from a WSDL. The name of the utility is wsimport. It can be found under $JAVA_HOME/bin. The only required argument for wsimport is the URL of the WSDL corresponding to the web service, for example:

wsimport http://localhost:8080/calculatorservice/CalculatorService?wsdl

The preceding command will generate a number of compiled Java classes that allow client applications to access our web service:

  • Add.class
  • AddResponse.class
  • Calculator.class
  • CalculatorService.class
  • ObjectFactory.class
  • package-info.class
  • Subtract.class
  • SubtractResponse.class
Keeping generated source code: By default, the source code for the generated class files is automatically deleted; it can be kept by passing the -keep parameter to wsimport.

These classes need to be added to the client's CLASSPATH in order for them to be accessible to the client's code.

If we are using Apache Maven to build our code, we can take advantage of the JAX-WS Maven plugin to automatically invoke wsimport when building our client code. This approach is illustrated in the following pom.xml file:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
 
    <groupId>net.ensode.javaee8book</groupId> 
    <artifactId>calculatorserviceclient</artifactId> 
    <version>1.0</version> 
    <packaging>war</packaging> 
 
    <name>calculatorserviceclient</name> 
 
    <properties> 
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> 
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    </properties> 
     
    <dependencies> 
        <dependency> 
            <groupId>javax</groupId> 
            <artifactId>javaee-api</artifactId> 
            <version>7.0</version> 
            <scope>provided</scope> 
        </dependency> 
    </dependencies> 
 
    <build> 
        <plugins> 
            <plugin> 
                <groupId>org.codehaus.mojo</groupId> 
                <artifactId>jaxws-maven-plugin</artifactId> 
                <version>2.4.1</version> 
                <executions> 
                    <execution> 
                        <goals> 
                            <goal>wsimport</goal> 
                        </goals> 
                        <configuration> 
                            <vmArgs> 
                            <vmArg>-Djavax.xml.accessExternalSchema=all</vmArg> 
                            </vmArgs> 
                            <wsdlUrls> 
                               <wsdlUrl> 
                     http://localhost:8080/calculatorservice/CalculatorService?wsdl 
                              </wsdlUrl> 
                            </wsdlUrls> 
                            <keep>true</keep> 
                        </configuration> 
                    </execution> 
                </executions> 
            </plugin> 
           <!-- additional plugins removed for brevity --> 
        </plugins> 
    </build> 
</project> 

The preceding pom.xml Maven build file will automatically invoke the wsimport utility whenever we build our code via the mvn package or mvn install commands.

At this point, we are ready to develop a simple client to access our web service. We will implement our client as a JSF application. The most relevant parts of our client application source code are shown as follows:

package net.ensode.javaee8book.calculatorserviceclient;
import javax.enterprise.context.RequestScoped;
import javax.faces.event.ActionEvent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.xml.ws.WebServiceRef;
import net.ensode.javaee8book.jaxws.Calculator;
import net.ensode.javaee8book.jaxws.CalculatorService;
@Named
@RequestScoped
public class CalculatorClientController {
@WebServiceRef(wsdlLocation =
"http://localhost:8080/calculatorservice/CalculatorService?wsdl")

private CalculatorService calculatorService;

@Inject

private CalculatorServiceClientModel
calculatorServiceClientModel;

private Integer sum;

private Integer difference;

public void add(ActionEvent actionEvent) {

Calculator calculator =
calculatorService.getCalculatorPort();

sum = calculator.add(calculatorServiceClientModel.getAddend1(),

calculatorServiceClientModel.getAddend2());

}

public void subtract(ActionEvent actionEvent) {

Calculator calculator =
calculatorService.getCalculatorPort();

difference =
calculator.subtract(calculatorServiceClientModel.getMinuend(),

calculatorServiceClientModel.getSubtrahend());

}

public Integer getSum() {

return sum;

}

public void setSum(Integer sum) {

this.sum = sum;

}

public Integer getDifference() {

return difference;

}

public void setDifference(Integer difference) {

this.difference = difference;

}
}

The @WebServiceRef annotation injects an instance of the web service into our client application. Its wsdlLocation attribute contains the URL of the WSDL corresponding to the web service we are invoking.

Notice that the web service class is an instance of a class called CalculatorService. This class was created when we invoked the wsimport utility, as wsimport always generates a class whose name is the name of the class we implemented plus the service suffix. We use this service class to obtain an instance of the web "Service" class we developed. In our example, we do this by invoking the getCalculatorPort() method on the CalculatorService instance. In general, the method to invoke to get an instance of our web service class follows the pattern getNamePort(), where Name is the name of the class we wrote to implement the web service. Once we get an instance of our web service class, we can simply invoke its methods as with any regular Java object.

Strictly speaking, the getNamePort() method of the service class returns an instance of a class implementing an interface generated by wsimport. This interface is given the name of our web service class and declares all of the methods we declared to be web services. For all practical purposes, the object returned is equivalent to our web service class.

The user interface for our simple client application is developed using Facelets, as customary when developing JSF applications:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://xmlns.jcp.org/jsf/html" 
      xmlns:f="http://xmlns.jcp.org/jsf/core"> 
    <h:head> 
        <title>Calculator Service Client</title> 
    </h:head> 
    <h:body> 
        <h3>Simple JAX-WS Web Service Client</h3> 
        <h:messages/> 
        <h:form> 
            <h:panelGrid columns="4"> 
                <h:inputText id="addend1"
                   value="#{calculatorServiceClientModel.addend1}"/> 
                <h:inputText id="addend2" 
                  value="#{calculatorServiceClientModel.addend2}"/> 
                <h:commandButton value="Add" 
                  actionListener="#  
{calculatorClientController.add}"> <f:ajax execute="addend1 addend2"
render="sum"/> </h:commandButton> <h:panelGroup> Total: <h:outputText id="sum" value="#{calculatorClientController.sum}"/> </h:panelGroup> </h:panelGrid> <br/> <h:panelGrid columns="4"> <h:inputText id="minuend" value="#{calculatorServiceClientModel.minuend}"/> <h:inputText id="subtrahend" value="#
{calculatorServiceClientModel.subtrahend}"/> <h:commandButton value="Subtract" actionListener="#
{calculatorClientController.subtract}"> <f:ajax execute="minuend subtrahend"
render="difference"/> </h:commandButton> <h:panelGroup> Difference: <h:outputText id="difference" value="#
{calculatorClientController.difference}"/> </h:panelGroup> </h:panelGrid> </h:form> </h:body> </html>

The user interface uses Ajax to invoke the relevant methods on the CalculatorClientController CDI named bean (refer to Chapter 2, JavaServer Faces, for details).

After deploying our code, our browser should render our page as follows (shown after entering some data and clicking the corresponding buttons):

In this example, we passed Integer objects as parameters and return values. Of course, it is also possible to pass primitive types both as parameters and as return values. Unfortunately, not all standard Java classes or primitive types can be used as method parameters or return values when invoking SOAP-based web services implemented via JAX-WS. The reason for this is that, behind the scenes, method parameters and return types get mapped to XML definitions, and not all types can be properly mapped.

Valid types that can be used in JAX-WS web service calls are listed here:

  • java.awt.Image
  • java.lang.Object
  • Java.lang.String
  • java.math.BigDecimal
  • java.math.BigInteger
  • java.net.URI
  • java.util.Calendar
  • java.util.Date
  • java.util.UUID
  • javax.activation.DataHandler
  • javax.xml.datatype.Duration
  • javax.xml.datatype.XMLGregorianCalendar
  • javax.xml.namespace.QName
  • javax.xml.transform.Source

Additionally, the following primitive types can be used:

  • Boolean
  • byte
  • byte[]
  • double
  • float
  • int
  • long
  • short

We can also use our own custom classes as method parameters and/or return values for web service methods, but member variables of our classes must be one of the listed types.

Additionally, it is legal to use arrays both as method parameters and return values; however, when executing wsimport, these arrays get converted to lists, generating a mismatch between the method signature in the web service and the method call invoked in the client. For this reason, it is preferred to use lists as method parameters and/or return values, since this is also legal and does not create a mismatch between the client and the server.

JAX-WS uses the Java Architecture for XML Binding (JAXB) internally to create SOAP messages from method calls. The types we are allowed to use for method calls and return values are the ones that JAXB supports. For more information on JAXB, see https://github.com/javaee/jaxb-v2.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.197.10