Showing posts with label WSDL. Show all posts
Showing posts with label WSDL. Show all posts

Tuesday, February 7, 2012

Maven and Spring WS - contract first

In this post I am going to show you how to configure and create a Web Service using Spring WS. 
Web Services developed with Spring are called contract-first Web Services because these starts with the XML Schema/WSDL contract first followed  by the Java code. In the first part of this tutorial I show you how to create the contract of the web service with XML and XSD containing request and response objects. In the second part I show you how to imlement the Web Service with Spring WS.

The most important thing when doing contract-first Web Services is to think in terms of XML. It is the XML that is sent across the wire, and you should focus on that- The fact that Java is used to implement the Web Service is an implementation detail.

The Web Service I am going to create is used to call the Google Maps services that returns an image of a locality. The reaquest contains an address or coordinates of a point in the earth or the request contains addresses or coordinates of 2 points and the response image contains the route to get from the origin to destination.

Messages
In this section I will focus on the XML messages that are sent to and from the Web Service.

As I wrote above, two of the four requests sent to the Web Service contain a coordinate (latitude, longitude) of a point in the earth or a String address to be searched by the Google Maps services, this is the declaration of these kind of objects using XSD: 

Below an example of these two XML object types: 








The next two request types are just a little different from the two above. These will contain two coordinates or two addresses to calculate the route to get from origin to destination. The last type has also another attribute called waypoints that is a List of intermediate addresses which to pass through to get to destination: 

 Next an example of these two object requests: 











The service contract based on the XSD types above is a WSDL file. It is not required that we write it by hand because Spring creates it for us when we deploy the Web Service in a Application Server. 

Web Service configuration
We can now create a new project to implements the Web Service using Java. 
I will use Maven 2 to create the initial project structure.
Once created the project structure, under the src/main/webapp folder we will find the standard web application deployment descriptor WEB-INF/web.xml which defines a Spring-WS MessageDispatcherServlet and maps all incoming requests to this servlet.

In addition to the above WEB-INF/web.xml file, we need to create the Spring configuration file WEB-INF/spring-ws-servlet.xml. This file contains all the Spring beans related to the WS such as the Web Service Implementation, the Endpoint and the contracts of the Web Service (XSD and WSDL). The name of this file is derived from the name of the attendant servlet (in this case spring-ws) with -servlet.xml appended to it.  Below we can see the configuration file: 
The first bean I have declared is a PropertyPlaceholder used to import some default values from a properties file declared in a folder on the root of the Application Server. The other bean declared are the Service Implementation that injects some default properties, by Inversion of Control, that get the values from the property placeholders from the file imported above. Follows the Endpoint declaration which injects the web service bean to a constructor argument. The last two beans represent the contract of the Web Service: XSD definition and WSDL definition that gets some properties to be injected. 

There's another configuration file that we need to create: it is the pom.xml that is the Maven configuration file used to list the dependencies of the application and information such as the build process. Follows my pom.xml: 

The dependencies needed for this project are Spring-WS and the libraries needed to test the application. There is also the maven plugin for the build process. 

The Endpoint
In Spring WS, we need to implement Endpoints to handle incoming XML requests. It is created by annotating the class with the @Endpoint annotation and it has one or more methods to handle the incoming requests each related to a Web Service method. 
Before creating the Endpoint class I have used JAX-B to create the Java classes related to the XSD types I have defined earlier for the requests and responses. 
The command to be used to generate the classes is: xjc SchemaFile.xsd.
Follows my web service endpoint: 


package com.faeddalberto.googlews.endpoint;

@Endpoint
public class GoogleServicesEndpoint {

    private GoogleServices service;
 
    @Autowired
    public GoogleServicesEndpoint(GoogleServices service) {
        this.service = service;
    }
 
    @PayloadRoot(localPart="ImageFromCoordinatesRequest", 
        namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
    public GoogleServicesResponse getImageFromCoordinates(
            ImageFromCoordinatesRequest aRequest){

        return service.getImageFromCoordinates(aRequest);
    }

    @PayloadRoot(localPart="ImageFromAddressRequest", 
        namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
    public GoogleServicesResponse getImageFromAddress(
            ImageFromAddressRequest aRequest){

        return service.getImageFromAddress(aRequest);
    }
 
    @PayloadRoot(localPart="ImageRouteFromCoordinatesRequest",
        namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
    public GoogleServicesResponse getImageRouteFromCoordinates(
            ImageRouteFromCoordinatesRequest aRequest){

        return service.getImageRouteFromCoordinates(aRequest);
    }
 
    @PayloadRoot(localPart="ImageRouteFromAddressesRequest",
        namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
    public GoogleServicesResponse getImageRouteFromAddresses(
            ImageRouteFromAddressesRequest aRequest){

        return service.getImageRouteFromAddresses(aRequest);
    }
}

This Endpoint class, annotated with the @Endpoint annotation, has a constructor to which is injected the Web Service Implementation Bean as I showed you in the Spring configuration file.
The @PayloadRoot annotations tells Spring WS that the methods are suitable for handling XML messages.
This annotation uses some attributes: the localpart attribute that defines which is the request that the method handles and the namespace attribute that defines the namespace which the request is related to. The four methods declared in the Endpoint class call the four web service methods and all four get a different request as argument that are the classes defined by the JAX-B APIs from the XSD.
All the methods return the same response that is the response of the web service: an image related to the request or an error message if any error has occurred in the web service.

Implementing the Web Service
Let's now give a look at the Java code to implement the Web Service. As usual we have the Interface that declares the service operations and the Service Implementation Bean that implements these operations.
Follows the web service interface:

package com.faeddalberto.googlews.service;

public interface GoogleServices {

    public GoogleServicesResponse getImageFromCoordinates(
ImageFromCoordinatesRequest request);
 
    public GoogleServicesResponse getImageFromAddress(
        ImageFromAddressRequest request);
 
    public GoogleServicesResponse getImageRouteFromCoordinates(
        ImageRouteFromCoordinatesRequest request);
 
    public GoogleServicesResponse getImageRouteFromAddresses(
        ImageRouteFromAddressesRequest request);

}


The Web Service Interface has four methods declared that are the methods the Web Service Implementation Bean implements:


package com.faeddalberto.googlews.service;

public class GoogleServicesImpl implements GoogleServices {

    private static final String SENSOR = "sensor=true";
    private static final String QUESTION_MARK = "?";
    private static final String AMPERSAND = "&";
    private static final String PIPE = "|"; 

    private String directionUrl;
    private String staticUrl;
 
    private String mapType;
    private String zoom;
    private String size;
    private String path;
    private String markers;
 
    private String center;
    private String origin;
    private String destination;
    private String wayPoints; 
 
    public GoogleServicesResponse getImageFromCoordinates(
            ImageFromCoordinatesRequest request) {
  
        System.out.println("getImageFromCoordinates");
  
        GoogleServicesResponse response = new GoogleServicesResponse();
  
        center += request.getLatitude() + "," + request.getLongitude();
        markers += request.getLatitude() + "," + request.getLongitude();
  
        staticUrl += QUESTION_MARK + size + AMPERSAND + zoom + 
            AMPERSAND + center + AMPERSAND + markers + AMPERSAND + 
                mapType + AMPERSAND + SENSOR;
  
        byte[] imageBytes = null;
        try {
            imageBytes = HttpUtils.loadByteArrayImageFromHttp(staticUrl);
        } catch (IOException e) {
            System.err.println("IOException " + e.getMessage());
            response.setErrorDescription("An error has 
                occurred while creating the image");
        }
  
        response.setImage(imageBytes);
  
        return response;
    }

 
    public GoogleServicesResponse getImageFromAddress(
            ImageFromAddressRequest request) {

        System.out.println("getImageFromAddress");
  
        GoogleServicesResponse response = new GoogleServicesResponse();
  
        center += request.getAddress().replace(" ", "+");
  
        staticUrl += QUESTION_MARK + size + AMPERSAND + zoom +
            AMPERSAND + center + AMPERSAND + 
                mapType + AMPERSAND + SENSOR;
  
        byte[] imageBytes = null;
        try {
            imageBytes = HttpUtils.loadByteArrayImageFromHttp(staticUrl);
        } catch (IOException e) {
            System.err.println("IOException " + e.getMessage());
            response.setErrorDescription("An error has occurred 
                while creating the image");
        }
  
        response.setImage(imageBytes);
  
        return response;
    }

 
    public GoogleServicesResponse getImageRouteFromCoordinates(
            ImageRouteFromCoordinatesRequest request) {

        System.out.println("getImageRouteFromCoordinates");
        GoogleServicesResponse response = new GoogleServicesResponse();
  
        origin += request.getLatitudeOrg() + 
                        "," + request.getLongitudeOrg();
        destination += request.getLatitudeDst() + 
                        "," + request.getLongitudeDst();
    
        directionUrl += QUESTION_MARK + origin + AMPERSAND 
            + destination + AMPERSAND + SENSOR;
  
        try {
            response.setImage(getImageRoute());
        } catch (MalformedURLException e) {
            System.err.println("MalformedURLException " + e.getMessage());
            response.setErrorDescription("An error has occurred 
                while processing the request");
        } catch (IOException e) {
            System.err.println("IOException " + e.getMessage());
            response.setErrorDescription("An error has occurred 
                while creating the image containing the route");
        }
  
        return response;
    }

 
    public GoogleServicesResponse getImageRouteFromAddresses(
            ImageRouteFromAddressesRequest request) {

        GoogleServicesResponse response = new GoogleServicesResponse();
  
        System.out.println("getImageRouteFromAddresses");
  
        origin += request.getAddressOrg();
        destination += request.getAddressDst();
  
        for(int i = 0; i < request.getWaypoints().size(); i++) {
            if (i != 0) wayPoints += PIPE; 
            wayPoints += request.getWaypoints().get(i).replace(" ", "+");
        }
  
        directionUrl += QUESTION_MARK + origin + AMPERSAND + 
            destination + AMPERSAND + wayPoints + AMPERSAND + SENSOR;
    
        try {
            response.setImage(getImageRoute());
        } catch (MalformedURLException e) {
            System.err.println("MalformedURLException " + e.getMessage());
            response.setErrorDescription("An error has occurred
                    while processing the request");
        } catch (IOException e) {
            System.err.println("IOException " + e.getMessage());
            response.setErrorDescription("An error has occurred 
                    while creating the image containing the route");
        }
  
        return response;
    }
 
 
    private byte[] getImageRoute() 
            throws MalformedURLException, IOException {
  
        System.out.println(directionUrl);

        String xmlString = 
               HttpUtils.loadStringXmlFromHttp(directionUrl);  
  
        Document xmlDocumentResult = 
               XmlUtils.createXmlDocumentFromString(xmlString);
  
        String routeCoord = 
               XmlUtils.getRouteCoordinatesFromXml(xmlDocumentResult);
  
        staticUrl += QUESTION_MARK + size + AMPERSAND 
                  + path + routeCoord + AMPERSAND + SENSOR;
  
        byte[] imageBytes = null;
  
        imageBytes = HttpUtils.loadByteArrayImageFromHttp(staticUrl);

        return imageBytes;
    }

 
             /* GETTERS & SETTERS */
 
    public String getDirectionUrl() {
        return directionUrl;
    }


    public void setDirectionUrl(String directionUrl) {
        this.directionUrl = directionUrl;
    }


    public String getStaticUrl() {
        return staticUrl;
    }


    public void setStaticUrl(String staticUrl) {
        this.staticUrl = staticUrl;
    }


    public String getMapType() {
        return mapType;
    }


    public void setMapType(String mapType) {
        this.mapType = mapType;
    }


    public String getZoom() {
        return zoom;
    }


    public void setZoom(String zoom) {
        this.zoom = zoom;
    }


    public String getSize() {
        return size;
    }


    public void setSize(String size) {
        this.size = size;
    }


    public String getPath() {
        return path;
    }


    public void setPath(String path) {
        this.path = path;
    }


    public String getMarkers() {
        return markers;
    }


    public void setMarkers(String markers) {
        this.markers = markers;
    }


    public String getCenter() {
        return center;
    }


    public void setCenter(String center) {
        this.center = center;
    }


    public String getOrigin() {
        return origin;
    }


    public void setOrigin(String origin) {
        this.origin = origin;
    }


    public String getDestination() {
        return destination;
    }


    public void setDestination(String destination) {
        this.destination = destination;
    }


    public String getWayPoints() {
        return wayPoints;
    }


    public void setWayPoints(String wayPoints) {
        this.wayPoints = wayPoints;
    }
}


The Web Service Implementation bean has some constants and some instance variables with default values injected by the Spring context configuration file.
The implemented methods, after reading the request values make use of two classes, HttpUtils and XmlUtils, that I've implemented to call the Google Services using the HttpConnection, Stream, and XML related APIs to parse the response of the Google services.
These are the two utils classes that I am talking about:


The util class related to Http:
package com.faeddalberto.googlews.utils;

public class HttpUtils {

    public static String loadStringXmlFromHttp(
            String urlString) throws IOException, MalformedURLException {
  
        URL url = null;
        BufferedReader reader = null;
        StringBuilder stringBuilder = null;
  
        try {
            url = new URL(urlString);
 
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        
            // just want to do an HTTP GET here
            connection.setRequestMethod("GET");
            connection.setDoOutput(true);
            connection.connect();
   
            // read the output from the server
            reader = new BufferedReader(
                 new InputStreamReader(connection.getInputStream()));
            stringBuilder = new StringBuilder();
      
            String line = null;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line + "\n");
                System.out.println(line);
            }
      
        } finally {
        
            // close the reader; this can throw an exception too, so
            // wrap it in another try/catch block.
            if (reader != null)   {
                try {
                    reader.close();
                }  catch (IOException ioe) {
                    System.err.println("IOException " + ioe.getMessage());
                }
            }
        }
  
        return stringBuilder.toString();
    }
 
    public static byte[] loadByteArrayImageFromHttp(String urlString) throws IOException { 

        URL url = new URL(urlString); //Get an input stream for reading 

        HttpURLConnection hc = null;
        hc = (HttpURLConnection) url.openConnection();
        hc.setRequestMethod("GET");
        hc.setDoOutput(true);
        hc.connect();

        InputStream in = new BufferedInputStream(hc.getInputStream());
  
        try {
            ByteArrayOutputStream bout = new ByteArrayOutputStream(10000);
            int b;
            while ((b = in.read()) != -1) {
                bout.write(b);
            }
            
            return bout.toByteArray();
        } finally {
            in.close();
        }
    }

}



The util class related to XML:
package com.faeddalberto.googlews.utils;

public class XmlUtils {

    public static Document createXmlDocumentFromString(String result) {
  
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        InputSource source = new InputSource(new StringReader(result));
  
        Document document = null;
  
        try {
            document = factory.newDocumentBuilder().parse(source);
        } catch (SAXException e) {
            System.err.println("SAXException " + e.getMessage());
        } catch (IOException e) {
            System.err.println("IOException " + e.getMessage());
        } catch (ParserConfigurationException e) {
            System.err.println("ParserConfigurationException " + e.getMessage());
        }
   
        return document;
    }
 
    
    public static String getRouteCoordinatesFromXml(Document xmlDocumentResult) {
  
        String lat = null, lng = null, route = ""; 
  
        NodeList nList = xmlDocumentResult.getElementsByTagName("step");
  
        for (int temp = 0; temp < nList.getLength(); temp++) {
            Node nNode = nList.item(temp);
            if (nNode.getNodeType() == Node.ELEMENT_NODE) {

                Element eElement = (Element) nNode;

                System.out.println("LAT: " + getTagValue("lat", eElement));
                System.out.println("LONG: " + getTagValue("lng", eElement));
    
                lat = getTagValue("lat", eElement);
                lng = getTagValue("lng", eElement);
            }
   
            route += "|" + lat + "," + lng;
        }
  
        return route;
    }
 
    private static String getTagValue(String sTag, Element eElement) {
  
        NodeList nlList = 
             eElement.getElementsByTagName(sTag).item(0).getChildNodes();
   
        Node nValue = (Node) nlList.item(0);
  
        return nValue.getNodeValue();
    }
 
}


Next picture shows the browser with the resulting WSDL of the service calling http://127.0.0.1:8080/googlews/googleservices.wsdl. In the picture below we can only see the first part of the WSDL, containing the types associated with the service: 



The next thing I want to show you are the tests of the different web service operations and the images resulting from the calls. I have used jUnit to test the service methods:

1st method: Image from coordinates:
@Test
@DirtiesContext
public void getImageFromCoordinatesTest() throws IOException {
  
    GoogleServices services = 
               (GoogleServices) applicationContext.getBean("services");
  
    ImageFromCoordinatesRequest request = new ImageFromCoordinatesRequest();
    request.setLatitude(40.711614);
    request.setLongitude(-74.012318);
  
    String fileName = "lat " + request.getLatitude() + 
                      " lon " + request.getLongitude();
  
    GoogleServicesResponse response = services.getImageFromCoordinates(request); 
  
    Assert.assertNotNull(response.getImage());
  
    if (response.getImage() != null)
         createImageOnDisk(response.getImage(), fileName); 
    else System.out.println(response.getErrorDescription());
}




2nd method: Image from address:
@Test
@DirtiesContext
public void getImageFromAddressTest() throws IOException {
  
    GoogleServices services = 
         (GoogleServices) applicationContext.getBean("services");
  
    ImageFromAddressRequest request = new ImageFromAddressRequest();
    request.setAddress("Piccadilly Circus, London, UK");
  
    String fileName = request.getAddress().replace(',', ' ');
  
    GoogleServicesResponse response = services.getImageFromAddress(request);
  
    Assert.assertNotNull(response.getImage());
  
    if (response.getImage() != null) 
         createImageOnDisk(response.getImage(), fileName); 
    else System.out.println(response.getErrorDescription());
}



3rd method: Image route from coordinates origin and destination:
@Test
@DirtiesContext
public void getImageRouteFromCoordinatesTest() throws IOException {
 
    GoogleServices services = 
          (GoogleServices) applicationContext.getBean("services");
  
    ImageRouteFromCoordinatesRequest request =
            new ImageRouteFromCoordinatesRequest();
    request.setLatitudeOrg(37.4219720);
    request.setLongitudeOrg(-122.0841430);
    request.setLatitudeDst(37.4163228);
    request.setLongitudeDst(-122.0250403);
    
    String fileName = "latOrg " + request.getLatitudeOrg() + 
                      " lonOrg " + request.getLongitudeOrg() + 
                      "latDst " + request.getLatitudeDst() + 
                      " lonDst " + request.getLongitudeDst();
  
    GoogleServicesResponse response =
         services.getImageRouteFromCoordinates(request);
  
    Assert.assertNotNull(response.getImage());
  
    if (response.getImage() != null) 
        createImageOnDisk(response.getImage(), fileName); 
    else System.out.println(response.getErrorDescription());  
}



4th method: Image route from addresses origin and destination,  and eventually some waypoints:

@Test
@DirtiesContext
public void getImageRouteFromAddressesTest() throws IOException {
 
    GoogleServices services =
               (GoogleServices) applicationContext.getBean("services");
  
    ImageRouteFromAddressesRequest request =
          new ImageRouteFromAddressesRequest();
    request.setAddressOrg("Milano,MI");
    request.setAddressDst("Bologna,BO");
  
    List wayPoints = new ArrayList();
    wayPoints.add("Piacenza, PC");
    wayPoints.add("Modena, MO");
    request.setWaypoints(wayPoints);
  
    String fileName = request.getAddressOrg().replace(',', ' ') 
            + " - " + request.getAddressDst().replace(',', ' ');
  
    GoogleServicesResponse response =
               services.getImageRouteFromAddresses(request);
  
    Assert.assertNotNull(response.getImage());
  
    if (response.getImage() != null) 
        createImageOnDisk(response.getImage(), fileName); 
    else System.out.println(response.getErrorDescription());
}

Tuesday, November 22, 2011

JAX-WS SOAP Web Services - code first

In this post I am going to show you how to create a simple Java SOAP Web Service.

As the name suggests, a web service is a kind of webified application, that is, an application typically delivered over HTTP (Hyper Text Transport Protocol). A web service is thus a distributed application whose components can be deployed and executed on distinct devices.

Web services can be divided into two groups, SOAP-based and REST-style.
SOAP originally stood for Simple Object Access Protocol but, by serendipity, now may stand for Service
Oriented Architecture (SOA) Protocol. Whatever SOA may be, web services play a central role in the SOA approach to software design and development.  For now, SOAP is just an XML (EXtensible Markup Language) dialect in which documents are messages. In SOAP-based web services, the SOAP is mostly unseen infrastructure. For example, in a typical scenario, called the request/response message exchange pattern (MEP), the client’s underlying SOAP library sends a SOAP message as a service request, and the web service’s underlying SOAP library sends another SOAP message as the corresponding service response. The client and the web service source code may provide few hints about the underlying SOAP.

Except in test mode, the client of either a SOAP-based or REST-style service is rarely a web browser but rather an application without a graphical user interface. The client may be written in any language with the appropriate support libraries. Indeed, a major appeal of web services is language transparency: the service and its clients need not be written in the same language. Language transparency is the key to web service
interoperability; that is, the ability of web services and requesters to interact seamless despite differences in programming languages, support libraries, and platforms.

If a SOAP-based web service written in Java can have a Perl or a Ruby consumer, there must be an intermediary that handles the differences in data types between the service and the requester languages.
XML technologies, which support structured document interchange and processing, act as the intermediary. For example, in a typical SOAP-based web service, a client transparently sends a SOAP document as a request to a web service, which transparently returns another SOAP document as a response.


Calculator Web Service Example:


This is a simple SOAP web service that exposes four methods that are the main calculation operation. All the required libraries to compile, execute and consume Web Services are part of Standard Java 6, that supports JAX-WS (Java API for XML-Web Services).

A SOAP-based web service could be implemented as a single Java class but, following best practices, there should be an interface that declares the methods, which are the web service operations, and an implementation, which defines the methods declared in the interface. The interface is called the SEI: Service Endpoint Interface. The implementation is called the SIB: Service Implementation Bean. The SIB can be either a POJO or a Stateless Session EJB (Enterprise Java Bean).

The picture below shows the SEI (Service Endpoint Interface) for the Calculator Web Service.
It exposes the four web service's methods: add, substract, multiply, divide.






















The annotation @WebService signals that this is the SEI (Service Endpoint Interface).
@WebMethod signals that each method is a service operation.

The @SOAPBinding annotation impacts the under-the-hood construction of the service contract, the WSDL  (Web Services Definition Language) document. In my interface I used default values for this annotation, so it could be omitted. It specifies message style (DOCUMENT or RPC), encoding (LITERAL or SOAP ENCODED) and parameter style (WRAPPED or BARE).

Next picture shows the SIB (Service Implementation Bean) that implements the methods listed by the previous interface:


























The @WebService property endpointInterface links the SIB (this class) to the SEI (com.faeddalberto.calculator.CalculatorWSImpl). Note that the method implementations are not annotated as @WebMethods because those were already defined as web service methods in the interface.

The two files are compiled in the usual way from the current working directory, which in this case is immediately above the subdirectory "com". The symbol % represents the command prompt:

% javac com/faeddalberto/calculator/*.java


Now that we have successful compiled our web service we need to invoke, from the working directory,  the wsgen utility that comes with Java 6: 


% wsgen –cp . com.faeddalberto.calculator.CalculatorWSImpl

This utility generates some artifacts: java types needed by the Endpoint.publish to generate the service’s WSDL.
 The simplest way to publish a web service in a development/test environment is using a simple Java SE Application.

















This application publishes the web service whose SIB is com.faeddalberto.calculator.CalculatorWSImpl. For now, the service is published at network address 127.0.0.1., which is localhost, and the publication path is /calculator, an arbitrary name.
The Endpoint class has an overloaded publish method. In this two-argument version, the first argument is the  publication URL as a string and the second argument is an instance of the service SIB, in this case  com.faeddalberto.calculator.CalculatorWSImpl.
The application runs indefinitely, awaiting service requests. It needs to be terminated at the command prompt with control-C  or the equivalent.
Once the applicatation is started, open a browser to the URL  http://127.0.0.1/calculator?wsdl  to view the service contract, the WSDL document. This is an easy test to determine whether the service has deployed successfully. If the test succeeds, a client then can be executed against the service.

If everything is going well during the web service publication, you will see the following WSDL (Web Service Descriptor Language) on your browser page: 


In one of my next posts I'll describe the various parts of an WSDL document.

Associated to the Web Service contract there is also an XSD document describing the types for every request and response. If you open a browser to the URL http://127.0.0.1/calculator?xsd=1 you can see this: 


The types defined by the previou XSD are add, substract, multiply and divide (for the requests) and the same type name with the string Response attached for the web service responses. 
If you scroll above in the post you'll see that the wsgen utility used to create artifacts has created these types as Java classes: it means that that utility uses JAX-B to create java types associated to xsd elements needed by the web service to create the WSDL document and to marshal and unmarshal requests and responses. 

In full production mode, a Java Application Server such as BEA WebLogic, GlassFish, JBoss, or WebSphere might be used. To deploy our web service into an application server we need to create the WAR (Web Archive), and to do so we need to invoke the jar utility:

% jar cvf calculator.war WEB-INF

in which calculator.war is the name of the war we are creating and WEB-INF is the folder into which we have our class files to put into the war. Once created we can deploy it in the application server we choose and restart it. As we've done with our simple Jave SE application, once the applicatation server is started, open a browser to the URL  http://127.0.0.1/calculator?wsdl  to view the service contract, the WSDL document. This is an easy test to determine whether the service has deployed successfully.



Now that we have our web service up and running its time to call it and verify that it works.
We need now to create a web service client, and we can do it in different ways.
I show you a dynamic client and a static client both developed as a simple Java SE application but the same clients could also be used in a Java EE application Servlet or another utility class.

Dynamic client:

The first client I show you Is the dynamic solution:


This java class uses a URL object with a query string pointing to the WSDL location.
It then creates an XML qualified name, which has the syntax namespace URI:local name. A URI is a Uniform Resource Identifier and differs from the more common URL in that a URL specifies a location, whereas a URI need not specify a location. In short, a URI need not be a URL. In this example, the namespace URI is provided in the WSDL, and the local name is the SIB class name CalculatorWSImpl  with the word Service appended. The local name occurs in the service section, the last section of the WSDL document.
Once the URL and QName objects have been constructed and the Service.create method has been invoked, the statement of interest:

CalculatorWS calculator = service.getPort(CalculatorWS.class);

executes. In the WSDL document, the portType section describes, in the style of an interface, the operations included in the web service. The getPort method returns a reference to a Java object that can invoke the portType operations. The port object reference is of type com.faeddalberto.calculator.CalculatorWS, which is the
SEI type.

The Java client invokes the web service methods; and the Java libraries generate and process the SOAP messages exchanged transparently to enable the successful method invocations.


Static client:

In this section we will develop a standalone, static web service client to the Calculator web service
developed above. First all the static artifacts needed are generated using the wsimport tool. Note that the Calculator web service must be running when generating the artifacts, in order for wsimport to be able to access the WSDL document of the service.

% wsimport -p com.faeddalberto.calculator.staticclient -keep http://127.0.0.1/calculator?wsdl

This utility generates various classes in the subdirectory com.faeddalberto.calculator.staticclient (the -p flag stands for package). These classes make it easier to write a client against the service.
The list of the generated artifacts: 


Three points about these generated source files deserve mention. First, the interface CalculatorWS declares the very same methods as the original SEI. The methods are the web service operation add, substract, multiply, divide. Second, the class CalculatorWSImplService has a no-argument constructor that constructs the very same Service object as the original Java client DynamicCalculatorClient. Third, CalculatorWSImplService encapsulates the getcalculatorWSImplPort method, which returns an instance of type CalculatorWS, which in turn supports invocations of the four web service operations. Together the two generated types, the interface CalculatorWS and the class CalculatorWSImplService, ease the task
of writing a Java client against the web service. Next picture shows a client that uses the client-support code from the wsimport utility:


















This client is functionally equivalent to the previous, but this client is far easier to write. In particular, trouble some yet critical details such as the appropriate QName and service endpoint now are hidden in the wsimport-generated class. The idiom that is illustrated here works in general for writing clients with help from WSDL-based artifacts such as CalculatorWS and CalculatorWSImplService:

  • First, construct a Service object using one of two constructors in the wsimport generated class, in this example client.CalculatorWSImplService. The no-argument constructor is preferable because of its simplicity. However, a two-argument constructor is also available in case the web service’s namespace (URI) or the service endpoint (URL) have changed. Even in this case, however, it would be advisable to regenerate the WSDL-based Java files with another use of the wsimport utility.
  • Invoke the get...Port method on the constructed Service object, in this example, the method getCalculatorWSImpllPort. The method returns an object that encapsulates the web service operations declared in the original SEI.


Next picture shows the result for this client:


This was just a simple web service. There are different other ways of writing web services and clients against them, I'll show you on my next posts.