November 23, 2014
Hot Topics:

WSDL Essentials

  • February 25, 2003
  • By Developer.com Staff
  • Send Email »
  • More Articles »

XML Schema Data Typing

In order for a SOAP client to communicate effectively with a SOAP server, the client and server must agree on a data type system. By default, XML 1.0 does not provide a data type system. In contrast, every programming language provides some basic facility for declaring data types, such as integers, floats, doubles, and strings. One of the greatest challenges in building web services is therefore creating a common data type system that can be used by a diverse set of programming languages running on a diverse set of operating systems.

WSDL does not aim to create a standard for XML data typing. In fact, WSDL is specifically designed for maximum flexibility and is therefore not tied exclusively to any one data type system. Nonetheless, WSDL does default to the W3C XML Schema specification. The XML Schema specification is also currently the most widely used specification for data typing.

The more you know about XML Schemas, the better you can understand complex WSDL files. A full discussion of XML Schemas is beyond the scope of this chapter. However, two facts are crucially important.

First, the XML Schema specification includes a basic type system for encoding most data types. This type system includes a long list of built-in simple types, including strings, floats, doubles, integers, time, and date. This list, shown in Table 6-1, is excerpted from the XML Schema Part 0: Primer (http://www.w3org/TR/2000/WD=xmlschema=0=20000407/). If your application sticks to these simple data types, there is no need to include the WSDL types element, and the resulting WSDL file is extremely simple. For example, our first two WSDL files use only strings and floats.

Table 6-1: A list of the main XML Schema built-in simple types

Simple type

Example(s)

string

Web Services

Boolean

true, false, 1, 0

float

-INF, -1E4, -0, 0, 12.78E-2, 12, INF, NaN

double

-INF, -1E4, -0, 0, 12.78E-2, 12, INF, NaN

decimal

-1.23, 0, 123.4, 1000.00

binary

100010

integer

-126789, -1, 0, 1, 126789

nonPositiveInteger

-126789, -1, 0

negativeInteger

-126789, -1

long

-1, 12678967543233

int

-1, 126789675

short

-1, 12678

byte

-1, 126

nonNegativeInteger

0, 1, 126789

unsignedLong

0, 12678967543233

unsignedInt

0, 1267896754

unsignedShort

0, 12678

unsignedByte

0, 126

positiveInteger

1, 126789

date

1999-05-31

time

13:20:00.000, 13:20:00.000-05:00

Second, the XML Schema specification provides a facility for creating new data types. This is important if you want to create data types that go beyond what is already defined within the Schema. For example, a service might return an array of floats or a more complex stock quote object containing the high, low, and volume figures for a specific stock. Whenever your service goes beyond the simple XML Schema data types, you must declare these new data types within the WSDL types element.

In the next two sections of this chapter, we present two specific examples of using XML Schemas to create new data types. The first focuses on arrays; the second focuses on a more complex data type for encapsulating product information.

Arrays

Example 6-6, shown later in this section, is a sample WSDL file that illustrates the use of arrays. This is the Price List Service we created in Chapter 5. The service has one public method, called getPriceList, which expects an array of string SKU values and returns an array of double price values.

The WSDL file now includes a types element. Inside this element, we have defined two new complex types. Very broadly, the XML Schema defines simple types and complex types. Simple types cannot have element children or attributes, whereas complex types can have element children and attributes. We have declared complex types in our WSDL file, because an array may have multiple elements, one for each value in the array.

The XML Schema requires that any new type you create be based on some existing data type. This existing base type is specified via the base attribute. You can then choose to modify this base type using one of two main methods: extension or restriction. Extension simply means that your new data type will have all the properties of the base type plus some extra functionality. Restriction means that your new data type will have all the properties of the base data type, but may have additional restrictions placed on the data.

In Example 6-6, we'll create two new complex types via restriction. For example:

<complexType name="ArrayOfString">
   <complexContent>
      <restriction base="soapenc:Array">
         <attribute ref="soapenc:arrayType" 
            wsdl:arrayType="string[]"/>
      </restriction>
   </complexContent>
</complexType>

Example 6-6: PriceListService.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="PriceListService"
   targetNamespace="http://www.ecerami.com/wsdl/PriceListService.wsdl"
   xmlns="http://schemas.xmlsoap.org/wsdl/"
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns="http://www.ecerami.com/wsdl/PriceListService.wsdl"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:xsd1="http://www.ecerami.com/schema">
 
   <types>
      <schema xmlns="http://www.w3.org/2001/XMLSchema"
         targetNamespace="http://www.ecerami.com/schema"
         xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
         xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
 
         <complexType name="ArrayOfString">
            <complexContent>
               <restriction base="soapenc:Array">
                  <attribute ref="soapenc:arrayType" 
                  wsdl:arrayType="string[]"/>
               </restriction>
            </complexContent>
         </complexType>
         <complexType name="ArrayOfDouble">
            <complexContent>
               <restriction base="soapenc:Array">
                  <attribute ref="soapenc:arrayType"
                  wsdl:arrayType="double[]"/>
               </restriction>
            </complexContent>
         </complexType>
      </schema>
   </types>
 
   <message name="PriceListRequest">
      <part name="sku_list" type="xsd1:ArrayOfString"/>
   </message>
 
   <message name="PriceListResponse">
      <part name="price_list" type="xsd1:ArrayOfDouble"/>
   </message>
 
   <portType name="PriceList_PortType">
      <operation name="getPriceList">
         <input message="tns:PriceListRequest"/>
         <output message="tns:PriceListResponse"/>
      </operation>
   </portType>
 
   <binding name="PriceList_Binding" type="tns:PriceList_PortType">
   <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="getPriceList">
      <soap:operation soapAction="urn:examples:pricelistservice"/>
         <input>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               namespace="urn:examples:pricelistservice"
               use="encoded"/>
         </input>
         <output>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               namespace="urn:examples:pricelistservice" use="encoded"/>
         </output>
      </operation>
   </binding>
   
   <service name="PriceList_Service">
      <port name="PriceList_Port" binding="tns:PriceList_Binding">
         <soap:address location="http://localhost:8080/soap/servlet/rpcrouter"/>
      </port>
   </service>
</definitions>

The WSDL specification requires that arrays be based on the SOAP 1.1 encoding schema. It also requires that arrays use the name ArrayOfXXX, where XXX is the type of item in the array. The previous example therefore creates a new type called ArrayOfString. This new type is based on the SOAP array data type, but it is restricted to holding only string values. Likewise, the ArrayOfDouble data type creates a new array type containing only double values.

When using the WSDL types element, you need to be particularly aware of XML namespace issues. First, note that the root schema element must include a namespace declaration for the SOAP encoding specification (http://schemas.xmlsoap.org/soap/encoding/). This is required because our new data types extend the array definition specified by SOAP.

Second, the root schema element must specify a targetNamespace attribute. Any newly defined elements, such as our new array data types, will belong to the specified targetNamespace. To reference these data types later in the document, you must refer back to the same targetNamespace. Hence, our definitions element includes a new namespace declaration:

xmlns:xsd1="http://www.ecerami.com/schema">

xsd1 matches the targetNamespace and therefore enables us to reference the new data types later in the document. For example, the message element references the xsd1:ArrayOfString data type:

<message name="PriceListRequest">
   <part name="sku_list" type="xsd1:ArrayOfString"/>
</message>

TIP:  For an excellent and concise overview of W3C Schema complex types and their derivation via extension and restriction, see Donald Smith's article on "Understanding W3C Schema Complex Types." The article is available online at http://www.xml.com/pub/a/2001/08/22/easyschema.html.

Automatically invoking array services

Once you move beyond basic data types, the simple WSDL invocation methods described previously in this chapter no longer work quite as easily. For example, you cannot simply open the GLUE console, pass an array of strings, and hope to receive back an array of doubles. Additional work is necessary, and some manual code is required. Nonetheless, the additional work is minimal, and the discussion that follows focuses on the GLUE platform. We have chosen to focus on the GLUE platform because it represents the most elegant platform for working with complex data types; other tools, such as the IBM Web Services Toolkit, do, however, provide similar functionality.

To get started, you should become familiar with the GLUE wsdl2java command-line tool. The tool takes in a WSDL file and generates a suite of Java class files to automatically interface with the specified service. You can then write your own Java class to invoke the specified service. Best of all, the code you write is minimally simple, and all SOAP-specific details are completely hidden from your view. (See Figure 6-12.)

Figure 6-12. The GLUE wsdl2java tool and the GLUE architecture

 

Here is the wsdl2java command-line usage:

usage: wsdl2java <arguments>
 
where valid arguments are:
  http://host:port/filename     URL of WSDL
  -c                            checked exceptions
  -d directory                  output directory for files
  -l user password realm        login credentials
  -m map-file                   read mapping instructions
  -p package                    set default package
  -v                            verbose
  -x command-file               command file to execute

Complete information on each argument is available online within the GLUE User Guide at http://www.themindelectric.com/products/glue/releases/GLUE-1.1/docs/guide/index.html. For now, we will focus on the most basic arguments. For example, to generate Java class files for the PriceListService.wsdl file, first make sure that the WSDL file is available publicly on a web site or locally via a web server such as Tomcat. Then, issue the following command:

wsdl2java.bat http://localhost:8080/wsdl/PriceListService.wsdl -p com.
   ecerami.wsdl.glue

The first argument specifies the location of the WSDL file; the second argument specifies that the generated files should be placed in the package com.ecerami.wsdl.glue.

GLUE will automatically download the specified WSDL file and generate two Java class files:

write file IPriceList_Service.java
write file PriceList_ServiceHelper.java

The first file, IPriceList_Service.java, is shown in Example 6-7. This file represents a Java interface that mirrors the public methods exposed by the WSDL file. Specifically, the interface shows a getPriceList( ) method that receives an array of String values, and returns an array of double values.

Example 6-7: IPriceList_Service.java

// generated by GLUE
 
package com.ecerami.wsdl.glue;
 
public interface IPriceList_Service
  {
  double[] getPriceList( String[] sku_list );
  }

The second file, PriceList_ServiceHelper.java, is shown in Example 6-8. This is known as a GLUE helper file, and it can dynamically bind to the service specified by the WSDL file. To access the service, simply call the static bind( ) method.

Example 6-8: PriceList_ServiceHelper.java

// generated by GLUE
 
package com.ecerami.wsdl.glue;
 
import electric.registry.Registry;
import electric.registry.RegistryException;
 
public class PriceList_ServiceHelper
  {
  public static IPriceList_Service bind(  ) throws RegistryException
    {
    return bind( "http://localhost:8080/wsdl/PriceListService.wsdl" );
    }
 
  public static IPriceList_Service bind( String url ) 
    throws RegistryException
    {
    return (IPriceList_Service) 
       Registry.bind( url, IPriceList_Service.class );
    }
  }

Once GLUE has generated the interface and helper files, you just need to write your own class that actually invokes the service. Example 6-9 shows a sample application that invokes the Price List Service. The code first calls PriceList_ServiceHelper.bind( ), which then returns an IPriceList_Service object. All subsequent code behaves as if the Price List Service is a local object, and all SOAP-specific details are completely hidden from the developer.

Here is a sample output of the Invoke_PriceList application:

Product Catalog
SKU:  A358185 --> Price:  54.99
SKU:  A358565 --> Price:  19.99

Example 6-9: Invoke_PriceList.java

package com.ecerami.wsdl;
 
import com.ecerami.wsdl.glue.*;
 
/**
 * SOAP Invoker.  Uses the PriceListServiceHelper to invoke
 * SOAP service.  PriceListServiceHelper and IPriceListService
 * are automatically generated by GLUE.
*/
public class Invoke_PriceList {
 
  /**
   *  Get Product List via SOAP
   */
  public double[] getPrices (String skus[]) throws Exception {
    IPriceList_Service priceListService = PriceList_ServiceHelper.bind(  );
    double[] prices = priceListService.getPriceList(skus);
    return prices;
  }
 
  /**
   *  Main Method
   */
  public static void main (String[] args) throws Exception {
    Invoke_PriceList invoker = new Invoke_PriceList(  );
    System.out.println ("Product Catalog");
    String skus[] = {"A358185", "A358565" };
    double[] prices = invoker.getPrices (skus);
    for (int i=0; i<prices.length; i++) {
      System.out.print ("SKU:  "+skus[i]);
      System.out.println (" --> Price:  "+prices[i]);
    }
  }
}




Page 3 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel