Tutorial: Accessing web services from JAX-WS

The following tutorial demonstrates how to access web services using the Java API for XML Web Services (JAX-WS).

Before you begin, you will need the JAX-WS tools available from Sun. If you do not have this package installed on your system, you must download the JAX-WS tools and install them. To download the JAX-WS tools, visit [external link] http://java.sun.com/webservices/. Click the link for JAX-WS. This should take you to the [external link] Java API for XML Web Services page. Click the Download Now link. After you have downloaded the software package, install it on your system.

This example was developed with JAX-WS 2.1.3 for Windows.

SQL Anywhere SOAP web services that are accessed from JAX-WS should be declared to be of format CONCRETE.

To create SOAP and DISH services

  1. At a command prompt, execute the following statement to start a personal web server. The -xs http(port=80) option tells the database server to accept HTTP requests on port 80. If you already have a web server running on port 80, use another port number such as 8080 for this tutorial.

    dbeng11 -xs http(port=80) samples-dir\demo.db
  2. Start Interactive SQL and connect to the SQL Anywhere sample database as the DBA. Execute the following statements:

    1. Define a stored procedure that lists some columns of the Employees table.
      CREATE PROCEDURE ListEmployees()
      RESULT (
       EmployeeID            INTEGER,
       Surname               CHAR(20),
       GivenName             CHAR(20),
       StartDate             DATE,
       TerminationDate       DATE )
      BEGIN
        SELECT EmployeeID, Surname, GivenName, 
          StartDate, TerminationDate 
        FROM Employees;
      END;
    2. Define a SOAP service that calls this stored procedure.
      CREATE SERVICE "WS/EmployeeList"
      TYPE 'SOAP'
      FORMAT 'CONCRETE' EXPLICIT OFF
      DATATYPE OUT
      AUTHORIZATION OFF
      SECURE OFF
      USER DBA
      AS CALL ListEmployees();

      The EXPLICIT clause can only be used with a SOAP or DISH service of type CONCRETE. In this example, EXPLICIT OFF indicates that the corresponding DISH service should generate XML Schema that describes the generic SimpleDataset object. This option only affects the WSDL document that is generated. Later on, we will look at an example that uses EXPLICIT ON. See Tutorial: Using data types with JAX-WS.

      DATATYPE OUT indicates that explicit data type information is generated in the XML result set response. If DATATYPE OFF had been specified, then all data would be typed as string. This option does not affect the WSDL document that is generated.

      Because authorization has been turned off, anyone can use this service without supplying a user name and password. The commands run under user DBA. This arrangement is simple, but not secure.

    3. Create a DISH service to act as a proxy for the SOAP service and to generate the WSDL document.
      CREATE SERVICE "WSDish"
      TYPE 'DISH'
      FORMAT 'CONCRETE'
      GROUP "WS"
      AUTHORIZATION OFF
      SECURE OFF
      USER DBA;
      

      The SOAP and DISH service must both be format CONCRETE. Since the EmployeeList service is in the WS group, a GROUP clause is included.

  3. Take a look at the WSDL that the DISH service automatically creates. To do so, open a web browser and browse to the following URL: [external link] http://localhost:80/demo/WSDish. The DISH service automatically generates a WSDL document that appears in the browser window.

    In particular, observe the SimpleDataset object that is exposed because the format of this service is CONCRETE and EXPLICIT is OFF. In a later step, the wsimport application uses this information to generate a SOAP 1.1 client interface for this service.

    <s:complexType name="SimpleDataset">
    <s:sequence>
    <s:element name="rowset">
      <s:complexType>
      <s:sequence>
      <s:element name="row" minOccurs="0" maxOccurs="unbounded">
        <s:complexType>
        <s:sequence>
          <s:any minOccurs="0" maxOccurs="unbounded" /> 
        </s:sequence>
        </s:complexType>
      </s:element>
      </s:sequence>
      </s:complexType>
    </s:element>
    </s:sequence>
    </s:complexType>

To generate a JAX-WS interface for these web services

  1. In this example, the Java API for XML Web Services (JAX-WS) and the Sun Java 1.6.0 JDK are installed on drive C:. Set your PATH environment variable so that it includes the JAX-WS binaries, as well as the JDK binaries. The binaries are located in the following directories:

    c:\Sun\jaxws-ri\bin
    c:\jdk1.6.0\bin
  2. Set your CLASSPATH environment variable.

    SET classpath=.;C:\Sun\jaxws-ri\lib\jaxb-api.jar
    ;C:\Sun\jaxws-ri\lib\jaxws-rt.jar
  3. The next step is to generate the interface needed to call the web service.

    At a command prompt, execute the following command.

    wsimport -keep -Xendorsed "http://localhost:80/demo/WSDish"

    The wsimport tool retrieves the WSDL document from the given URL, generates the Java files that define the interface for it, and then compiles the Java files.

    The keep option tells wsimport not to delete the .java files. Without this option, these files are deleted after the corresponding .class files have been generated. Saving these files makes it easer to examine the makeup of the interface.

    The Xendorsed option allows you to use the JAX-WS 2.1 API with JDK6.

    Once this command completes, you should have a new subdirectory structure named localhost\demo\ws in your current directory that contains the following Java files, along with the compiled .class versions of each Java file.

    EmployeeList.java
    EmployeeListResponse.java
    FaultMessage.java
    ObjectFactory.java
    package-info.java
    SimpleDataset.java
    WSDish.java
    WSDishSoapPort.java

To use the generated JAX-WS interface

  1. Save the following Java source code into SASoapDemo.java. Make sure that this file is located in the same directory containing the localhost subdirectory that was generated by the wsimport tool.

    // SASoapDemo.java illustrates a web service client that
    // calls the WSDish service and prints out the data.
    
    import java.util.*;
    import javax.xml.ws.*;
    import org.w3c.dom.Element;
    import org.w3c.dom.Node;
    import localhost.demo.ws.*;
    
    public class SASoapDemo
    {
      @WebServiceRef( wsdlLocation= "http://localhost:8080/demo/WSDish" )
      public static void main( String[] args )
      {
        try {
          WSDish service = new WSDish();
    
          Holder<SimpleDataset> response = new Holder<SimpleDataset>();
          Holder<Integer> sqlcode = new Holder<Integer>();
                
          WSDishSoapPort port = service.getWSDishSoap();
    
          // This is the SOAP service call to EmployeeList
          port.employeeList( response, sqlcode );
    
          localhost.demo.ws.SimpleDataset result = response.value;
          SimpleDataset.Rowset rowset = result.getRowset();
    
          List<SimpleDataset.Rowset.Row> rows = rowset.getRow();
    
          for ( int i = 0; i < rows.size(); i++ ) {
            SimpleDataset.Rowset.Row row = rows.get( i );
            List<Object> cols = row.getAny();
    
            System.out.println( "Number of columns=" + cols.size() );
    
            for ( int j = 0; j < cols.size(); j++ ) {
              // Column data is contained as a SOAPElement
              Element col = (Element)cols.get(j);
              System.out.print(col.getLocalName() + "=" );
              Node node = col.getFirstChild();
              if( node == null ) {
                System.out.println( "(null)" );
              } else {
                System.out.println( node.getNodeValue() );
              }
            }
            System.out.println();
          }
    
        }
        catch (Exception x) {
          x.printStackTrace();
        }
      }
    }

    If you chose to start the web server using a different port number such as 8080, then you will have to alter the import localhost source line to something like the following:

    import localhost._8080.demo.ws.*;
  2. Compile SASoapDemo.java.

    javac SASoapDemo.java
  3. Run the compiled class file.

    java SASoapDemo

    When the application sends its request to the web server, it receives an XML result set response that consists of an EmployeeListResult with a rowset containing several row entries. Also included in the response is the sqlcode result from executing the query. An example of the response is shown next.

    <tns:EmployeeListResponse>
     <tns:EmployeeListResult xsi:type='tns:SimpleDataset'>
      <tns:rowset>
        <tns:row>...</tns:row>
    .
    .
    .
        <tns:row>...</tns:row>
       </tns:rowset>
     </tns:EmployeeListResult>
     <tns:sqlcode>0</tns:sqlcode>
    </tns:EmployeeListResponse>

    Each row in the rowset is sent in a format similar to the following.

    <tns:row>
       <tns:EmployeeID xsi:type="xsd:int">1751</tns:EmployeeID>
       <tns:Surname xsi:type="xsd:string">Ahmed</tns:Surname>
       <tns:GivenName xsi:type="xsd:string">Alex</tns:GivenName>
       <tns:StartDate xsi:type="xsd:date">1994-07-12-04:00</tns:StartDate>
       <tns:TerminationDate xsi:type="xsd:date">2008-04-18-04:00
           </tns:TerminationDate>
    </tns:row>

    Note that the column name and data type are included.

    Using a proxy

    You can observe the response shown above through the use of proxy software that logs the XML message traffic. The proxy inserts itself between your client application and the web server.

    The EmployeeList result set is displayed as (type)column name=value pairs by the SASoapDemo application. Several lines of output similar to the following should be generated.

    The EmployeeList result set is displayed as column name=value pairs. Several lines of output similar to the following should be generated.

    Number of columns=4
    EmployeeID=102
    Surname=Whitney
    GivenName=Fran
    StartDate=1984-08-28-04:00
    
    Number of columns=4
    EmployeeID=105
    Surname=Cobb
    GivenName=Matthew
    StartDate=1985-01-01-05:00
    .
    .
    .
    Number of columns=4
    EmployeeID=1740
    Surname=Nielsen
    GivenName=Robert
    StartDate=1994-06-24-04:00
    
    Number of columns=5
    EmployeeID=1751
    Surname=Ahmed
    GivenName=Alex
    StartDate=1994-07-12-04:00
    TerminationDate=2008-04-18-04:00

    Note that the TerminationDate column is only sent when its value is not NULL. For this example, the last row in the Employees table was altered such that a non-NULL termination date was set.

    Also note that date values include an offset from UTC time. In the above sample data, the server is located in the North American Eastern time zone. This is 5 hours earlier than UTC time (-05:00) for dates when daylight savings is not in effect and 4 hours earlier than UTC time (-04:00) for dates when daylight savings is in effect.