Chapter 25. JAX-RPC Web Service Data Type

Table of Contents

25.1. Overview
25.2. Java and XML Type Mapping
25.2.1. Built-in Type Mapping
25.2.2. Arrays
25.2.3. User Defined Types: JAX-RPC Value Type
25.3. Using JAX-RPC Value Type
25.3.1. Creating a Web service that Uses JAX-RPC value type
25.3.2. Creating a Web Service Client that Uses JAX-RPC Value Type
25.4. Using Holder Classes
25.4.1. Built-in Holder Classes
25.4.2. Creating a Holder Class for User Defined Types
25.5. Exceptions and SOAP Faults
25.6. Mapping MIME Type to Data Handler Type
25.6.1. Using dataHandlerOnly Option in wsdl2java
25.7. Disabling Data Binding in Doc/Literal
25.7.1. Using noDataBinding Option in wsdl2java

This chapter describes several data type issues related to JAX-RPC web services.

This chapter describes the standard Java/XML data type mapping and JAX-RPC value type, which are used as web service parameters in a user-defined class. Next, it will describe the JAX-RPC holder classes for out or in/out parameters as well as how to send error messages to the web service clients.

25.1. Overview

This section describes several issues concerning the data types used for the web service operations and clients.

The following are the features of web service data types.

  • Java and XML type mapping

    To (de)serialize an XML instance into a Java object, there must be a type mapping between XML types and Java classes. JEUS web services follow the standard XML/Java mapping defined by the JAX-RPC Specification.

  • Using JAX-RPC value type

    The JAX-RPC value type is a type that is passed by value in a web service request or response. The JAX-RPC value type is typically represented as a user-defined JavaBeans component. JEUS web services provide the capability to use user-defined JavaBeans as a parameter and return type of a web service operation.

  • Using holders as in/out parameter

    The in/out parameter is the parameter of a web service operation that is used as input and output. The in/out parameter can be used when the web service operation needs to return multiple outputs. JEUS web services support the in/out parameters by using the standard JAX-RPC holder classes.

  • Using exception as SOAP Fault

    When an error occurs during the web service operation, it must be notified to the web service client. The SOAP standard defines the SOAP Fault for this purpose. JEUS web services support the standard SOAP Fault.

25.2. Java and XML Type Mapping

So far, web service operation examples in this chapter only used one type (string) for both parameter and return types. JEUS web services map Java types to XML/WSDL definitions. For example, a JEUS web service maps the java.lang.String class to the xsd:string XML data type.

This section describes the data types supported by JEUS web services, and the requirements that a type must meet in order to be supported by JEUS web services.

Note

Application developers do not need to know the details of these mappings, but they must note that not all Java classes can be used as a parameter or return type of a method.

25.2.1. Built-in Type Mapping

The following table describes the Java and XML data type mappings built into JEUS web services.

[Table 25.1] Built-in XML/Java Type Mapping

XML Data TypeJava Data Type
xsd:stringjava.lang.String
xsd:booleanboolean, java.lang.Boolean *
xsd:doubledouble, java.lang.Double *
xsd:floatfloat, java.lang.Float *
xsd:intint, java.lang.Integer
xsd:integerjava.math.BigInteger
xsd:longlong, java.lang.Long *
xsd:shortshort, java.lang.Short *
xsd:bytebyte, java.lang.Byte
xsd:Decimaljava.math.BigDecimal
xsd:base64Binarybyte[ ]
xsd:hexBinarybyte[ ]
xsd:QNamejavax.xml.rpc.namespace.QName
xsd:dateTimejava.util.Calendar
xsd:gYearMonthjava.util.Calendar
xsd:gYearjava.util.Calendar
xsd:gMonthDayjava.util.Calendar
xsd:anyURIjava.net.URI (JDK 1.4 or over) / java.lang.String (JDK 1.4)
xsd:durationjava.lang.String
xsd:namejava.lang.String
xsd:NCNamejava.lang.String
xsd:NMTOKENjava.lang.String
xsd:nomalizedStringjava.lang.String
xsd:timejava.util.Calendar
xsd:tokenjava.lang.String
xsd:unsignedByteshort
xsd:unsignedLongjava.math.BigInteger
xsd:unsignedIntlong
xsd:unsignedShortint
SOAP-ENC:base64byte[ ]
SOAP-ENC:stringjava.lang.String
SOAP-ENC:booleanboolean, java.lang.Boolean *
SOAP-ENC:doubledouble, java.lang.Double *
SOAP-ENC:floatfloat, java.lang.Float *
SOAP-ENC:intint, java.lang.Integer *
SOAP-ENC:longlong, java.lang.Long *
SOAP-ENC:shortshort, java.lang.Short *
SOAP-ENC:bytebyte, java.lang.Byte *
SOAP-ENC:intergerjava.math.BigInteger
SOAP-ENC:decimaljava.math.BigDecimal
SOAP-ENC:Arrayjava.math.BigDecimal


Note

1. The prefix xsd represents this XML namespace URI: (http://www.w3.org/2001/XMLSchema).

2. The prefix SOAP-EN represents this XML namespace URI: (http://schemas.xmlsoap.org/soap/encoding).

3. If the WSDL specifies that an object can be set to null, the caller can use 'xsd:nil' when sending or receiving data. Java primitive data types are replaced by their wrapper classes. In the previous table, the Java types followed by an asterisk (*) represent this.

The QName of the XML type can be used to specify a data type in a DII client

String NS_XSD = “http://www.w3.org/2001/XMLSchema”;
String XSD_DATETIME = new QName(NS_XSD, “dateTime”);
call.addParameter(“arg1”, XSD_DATETIME, PARAM_MODE_IN);

25.2.2. Arrays

JEUS web services also support arrays that are defined by the JAX-RPC types. For example, int[ ], String[ ], and multidimensional arrays, such as java.math.BigDecimal[ ][ ], are supported.

25.2.3. User Defined Types: JAX-RPC Value Type

JEUS web services also support any user-defined types implemented in an application. The JAX-RPC specification refers to such classes as 'value types'. To be supported by JEUS web services, user-defined classes must conform to the following rules.

  • It must have a public default no-argument constructor.

  • It must not implement (either directly or indirectly) the java.rmi.Remote interface.

  • The field types must be supported by JEUS web services.

The class may contain public, private, or protected fields. To pass a field value during a web service call, the field must meet the following conditions.

  • A public field cannot be final or transient.

  • A non-public field must have the corresponding getter and setter methods.

JavaBeans components, which conform to these rules, are also supported.

25.3. Using JAX-RPC Value Type

The user-defined type that conforms to the rules stated in "25.2.3. User Defined Types: JAX-RPC Value Type" of this manual, can be used as parameter or return type of a JEUS web service. Such user-defined type is called a JAX-RPC value type.

This section describes an example of using the JAX-RPC value type. The example 'CalcService' is a calculator that takes two numbers and an operator and returns the result as a number. A web service client will be created for the CalcService web service.

25.3.1. Creating a Web service that Uses JAX-RPC value type

The following is the source code for the CalcService class, ‘Calculator.java’.

[Example 25.1] <<Calculator.java>>

package calc;

public class Calculator implements CalculatorIF {
    public Calculator() { }
    public double calc(CalcData data) {
        String op = data.getOp();
        double num1 = data.getNum1();
        double num2 = data.getNum2();
        double ret = -9999.0;

        if (op.equals("plus")) {
            ret = num1 + num2;
        } else if (op.equals("minus")) {
            ret = num1 - num2;
        } else if (op.equals("mult")) {
            ret = num1 * num2;
        } else if (op.equals("div")) {
            if (num2 != 0)
                ret = num1 / num2;
        }
        return ret;
    }
}


The calc( ) method takes an argument of the CalcData type and returns '–9999.0' when an error occurs. A more detailed error handling procedures will be described later.

The following is the source code of CalcData.java.

[Example 25.2] <<CalcData.java>>

package calc;

public class CalcData {
    private double num1;
    private double num2;
    private String op;

    public CalcData() { }
    public double getNum1() { return num1; }
    public double getNum2() { return num2; }
    public String getOp() { return op; }

    public void setNum1(double n) { num1 = n; }
    public void setNum2(double n) { num2 = n; }
    public void setOp(String s) { op = s; }
}


The CalcData class meets the requirements of a JAX-RPC value type. This means that an instance of the CalcData class can be copied on the fly by value.

The following is the service endpoint interface file, 'CalculatorIF.java'.

[Example 25.3] <<CalculatorIF.java>>

package calc;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface CalculatorIF extends Remote {
    public double calc(CalcData data) throws RemoteException;
}


Execute the following command to compile the source code and generate web service artifacts.

ant compile

This command generates the class files under the build directory.

Execute the following command to create a deployable EAR file.

ant wsear

Now, deploy the created web service module. The following is the web service URL.

http://localhost:8088/Calculator1Service/Calculator1Service?wsdl

25.3.2. Creating a Web Service Client that Uses JAX-RPC Value Type

The following command generates a Proxy client for the 'CalcService' web service.

ant wsdl2java

Assume that the package name of the generated stub code is 'com.test.calc'.

The following is the source code for the client program.

[Example 25.4] <<CalcClient.java>>

import com.test.calc.*;
import javax.xml.rpc.soap.SOAPFaultException;

public class CalcClient {
    public static void main(String[] args) {
        CalcClient calc = new CalcClient();

        if (args.length != 3) {
            System.out.println("usage: java CalcClient num1 op num2");
            System.out.println(" where op is one of " + "'plus', 'minus',
                     'mult', 'div'");
            System.exit(1);
        }
        try {
            calc.run(args);
        } catch (SOAPFaultException e) {
            System.err.println("faultcode = " + e.getFaultCode());
            System.err.println("faultString = " + e.getFaultString());
        } catch (Exception e) {
            System.err.println(e.toString());
            e.printStackTrace();
        }
    }
    public void run(String[] args) throws Exception {
        CalculatorIF port = new
        Calculator1Service_Impl().getCalculatorIFPort();
        CalcData data = new CalcData();
        data.setNum1((new Double(args[0])).doubleValue());
        data.setNum2((new Double(args[2])).doubleValue());
        data.setOp(args[1]);

        double ret = port.calc(data);
        System.out.println(ret);
    }
}


Once a client code has been implemented, you can compile the client and stub codes by using the following command

ant build

Execute Ant task to execute the client

ant runclient

If executed successfully, the following result will be displayed.

2.0

25.4. Using Holder Classes

If a web service operation needs to return multiple values, the return value can be defined as a JAX-RPC value type, or one or more out or in/out parameters can be specified. The holder class is a helper class that can be used as an in/out parameter.

25.4.1. Built-in Holder Classes

JEUS web services provide JAX-RPC holder classes for simple data types. The standard holder classes provided by JAX-RPC are shown in the following table.

[Table 25.2] Built-in Holder Class

Holder ClassJava Data Type
javax.xml.rpc.holders.BooleanHolderboolean
javax.xml.rpc.holders.ByteHolderbyte
javax.xml.rpc.holders.ShortHoldershort
javax.xml.rpc.holders.IntHolderint
javax.xml.rpc.holders.LongHolderlong
javax.xml.rpc.holders.FloatHolderfloat
javax.xml.rpc.holders.DoubleHolderdouble
javax.xml.rpc.holders.BigDecimalHolderjava.math.BigDecimal
javax.xml.rpc.holders.BigIntegerHolderjava.math.BigInteger
javax.xml.rpc.holders.ByteArrayHolderbyte[ ]
javax.xml.rpc.holders.CalendarHolderjava.util.Calendar
javax.xml.rpc.holders.QNameHolderjavax.xml.namespace.QName
javax.xml.rpc.holders.StringHolderjava.lang.String


Use the value field of each holder class to access its data. The following is the modified source code for 'Caculator.java'.

[Example 25.5] << Calculator.java >>

package calc;

import javax.xml.rpc.holders.DoubleHolder;

public class Calculator implements CalculatorIF {
    public Calculator() { }

    public void calc(CalcData data, DoubleHolder result){
        String op = data.getOp();
        double num1 = data.getNum1();
        double num2 = data.getNum2();
        double ret = -9999.0;

        if (op.equals("plus")) {
            ret = num1 + num2;
        } else if (op.equals("minus")) {
            ret = num1 - num2;
        } else if (op.equals("mult")) {
            ret = num1 * num2;
        } else if (op.equals("div")) {
            if (num2 != 0)
                ret = num1 / num2;
        }
    
        result.value = ret;
    }
}


The source code imports the built-in holder class, javax.xml.rpc.holders.Double Holder.

import javax.xml.rpc.holders.DoubleHolder;

The signature of calc( ) method has also been modified. Its return type is void, and it accepts holder object as its second parameter.

public void calc(CalcData data, DoubleHolder result){
. . .
}

To access the value of the holder object, use the value field of the holder class.

result.value = ret;

The web service must be packaged and deployed before creating a web service client for the service. The client program, 'CalcClient.java', must also be modified.

The following shows the run( ) method in the client source code, 'CalcClient.java'.

[Example 25.6] << run( ) Method in CalcClient.java >>

    public void run(String[] args) throws Exception {
        CalculatorIF port = new CalcService_Impl().getCalculatorIFPort();
        CalcData data = new CalcData();
        data.setNum1((new Double(args[0])).doubleValue());
        data.setNum2((new Double(args[2])).doubleValue());
        data.setOp(args[1]);

        DoubleHolder ret = new DoubleHolder();
        port.calc(data, ret);
        System.out.println(ret.value);
    }


Caution

Make sure to re-generate the proxy source code before compiling 'CalcClient.java'.

25.4.2. Creating a Holder Class for User Defined Types

The following are the steps for creating a holder class for the user-defined class.

  1. Implement the javax.xml.rpc.holders.Holder interface.

  2. Name your class TypeHolder, where Type is the name of the class that the holder object holds. For example, a holder class for the CalcData class would be 'CalcDataHolder'.

  3. Declare your holder class as public.

  4. Create a public field called 'value', whose data type is the same as the Holder Class type.

  5. Create a default no-argument constructor that initializes the 'value' field to a default value.

  6. Create a constructor that sets the 'value' field to the value of the constructor's parameter.

The CalcService example will be modified to illustrate how to use a holder class of the JAX-RPC value type. This is the CalcData class that includes the result value as its member variable. A private member variable called 'result' and its getter/setter methods are added to the following class.

[Example 25.7] << CalcData.java >>

package calc;

public class CalcData {
    private double num1;
    private double num2;
    private String op;
    private double result;

    public CalcData() { }
    public double getNum1() { return num1; }
    public double getNum2() { return num2; }
    public String getOp() { return op; }
    public double getResult() { return result; }

    public void setNum1(double n) { num1 = n; }
    public void setNum2(double n) { num2 = n; }
    public void setOp(String s) { op = s; }
    public void setResult(double n) { result = n; }
}


The holder class of the CalcData class is called CalcDataHolder. The CalcDataHolder class has a public 'value' field whose data type is CalcData and two constructors that initialize the 'value' field.

[Example 25.8] << CalcDataHolder.java >>

package calc;
import javax.xml.rpc.holders.Holder;

public class CalcDataHolder implements Holder {
    public CalcData value;

    public CalcDataHolder() {
    }

    public CalcDataHolder(CalcData value) {
        this.value = value;
    }
}


The following is the source code in 'Calculator.java' that uses the holder class. The calc( ) method defines a parameter whose data type is CalcDataHolder. The public 'value' field is used to access the object that the holder object holds.

package calc;

public class Calculator implements CalculatorIF {
    public Calculator() { }
    
    public void calc(CalcDataHolder calcData) {
        CalcData data = calcData.value;
        String op = data.getOp();
        double num1 = data.getNum1();
        double num2 = data.getNum2();
        double ret = -9999.0;

        if (op.equals("plus")) {
            ret = num1 + num2;
        } else if (op.equals("minus")) {
            ret = num1 - num2;
        } else if (op.equals("mult")) {
            ret = num1 * num2;
        } else if (op.equals("div")) {
            if (num2 != 0)
                ret = num1 / num2;
        }
        data.setResult(ret);
        
        // The following line is not necessary in this case.
        // But we will use it to show the usage of holder class.
        calcData.value = data;
    }
}

The following is the client source code. The 'ant wsdl2java' command also generates holder classes for the client from WSDL document. Note that the holder classes generated in this way can be different from the one that is manually created.

import com.test.calc.*; // generated by ant process-wsdl 
import com.test.calc.holders.*; // generated by ant process-wsdl 

public class CalcClient {
    public static void main(String[] args) {
        CalcClient calc = new CalcClient();

        if (args.length != 3) {
            System.out.println("usage: java CalcClient num1 op num2");
            System.out.println(" where op is one of " + "'plus', 'minus', 
                  'mult', 'div'");
            System.exit(1);
        }

        try {
            calc.run(args);
        } catch (Exception e) {
            System.err.println(e.toString());
            e.printStackTrace();
        }
    }

    public void run(String[] args) throws Exception {
        CalculatorIF port = new Calculator3Service_Impl().getCalculatorIFPort();
        CalcData data = new CalcData();
        CalcDataHolder dataHolder = new CalcDataHolder(data);

        data.setNum1((new Double(args[0])).doubleValue());
        data.setNum2((new Double(args[2])).doubleValue());
        data.setOp(args[1]);
        
        port.calc(dataHolder);
        System.out.println(dataHolder.value.getResult());
    }
}

25.5. Exceptions and SOAP Faults

In JEUS web services, you can use the java.rmi.RemoteException or one that inherits the java.lang.Exception class as a SOAP fault. The SOAP fault is used to send error and/or status information through a SOAP Message. For more detailed information, refer to the '4.4 SOAP Fault' section of the SOAP 1.1 specification.

When an exception status needs to be sent to the web service client in the web service application, the web service operation can be implemented to throw a java.rmi.RemoteException or a user-defined exception. The RemotException that is thrown is automatically wrapped as a SOAP Fault and delivered to the web service client inside the SOAP message body.

In the CalcService example, set the return value to '–9999.0' when an error occurs as in the following.

[Example 25.9] << Calculator.java >>

package calc;

import java.rmi.RemoteException;

public class Calculator {
    public Calculator() { }
    public double calc(CalcData data)
        throws RemoteException, DevideByZeroException {
        String op = data.getOp();
        double num1 = data.getNum1();
        double num2 = data.getNum2();
        double ret = 0;

        if (op.equals("plus")) {
            ret = num1 + num2;
        } else if (op.equals("minus")) {
            ret = num1 - num2;
        } else if (op.equals("mult")) {
            ret = num1 * num2;
        } else if (op.equals("div")) {
            if (num2 != 0)
                ret = num1 / num2;
            else 
                throw new DevideByZeroException("divide by zero");
        } else {
            throw new RemoteException("invalid opertion : " + op);
        }
        return ret;
    }
}


The calc() methods throws a java.rmi.RemoteException when an unknown operator is used or the divisor is zero. In the web service client program, you can use the java.rmi.RemoteException or java.lang.Exception to catch this exception.

25.6. Mapping MIME Type to Data Handler Type

Web service clients can send an attachment via a SOAP message.

Although JAX-RPC specification defines a Java type mapping that corresponds to the MIME type attachment, web service clients can also map it to javax.activation.DataHandler type, regardless of the MIME type.

This section describes how to map a MIME type to a DataHandler type by using the WSDL-to-Java mapping tool.

25.6.1. Using dataHandlerOnly Option in wsdl2java

The following is an example of a web service WSDL that contains a MIME part.

<message name="submission">
    <part name="title" type="xsd:string" />
    <part name="price" type="xsd:float" />
    <part name="attachment" type="xsd:hexBinary" />
</message>
. . .
<operation name="submit">
    . . .
    <input>
        . . .
        <mime:part>
            <mime:content part="attachment" type="application/xml" />
        </mime:part>
        . . .
    </input>
    . . .
</operation>

By default, when a service endpoint interface is generated by using wsdl2java based on the previous WSDL, the MIME type 'application/xml' becomes mapped to the javax.xml.transform.Source type.

public interface SubmitBook extends java.rmi.Remote {
  public String submit(String title, float price,
                       javax.xml.transform.Source attachment)
                       throws java.rmi.RemoteException;
}

When the attribute called 'dataHandlerOnly' is set to 'true' through Ant task and wsdl2java, or when the '–datahandleronly' option is used through the command-line tool, the MIME part is always mapped to javax.activation. DataHandler type, regardless of the MIME type.

public interface SubmitBook extends java.rmi.Remote {
  public String submit(String title, float price,
                       javax.activation.DataHandler attachment)
                       throws java.rmi.RemoteException;
}

Hence, the web service client must send the attachment as the DataHandler type.

The following is an example of the web service client that uses the DataHandler type.

// Creates a FileInputStream from the specified path name
FileInputStream inputStream =
    new FileInputStream(new File("attachment/book.xml"));
DataHandler dataHandler = new DataHandler(inputStream, "application/xml");

// Get a Service port
SubmitBook port = new SubmitBookService_Impl().getSubmitBookPort();
String result = port.submit("Sample for a option: datahandleronly", 
                12.34f, dataHandler);
System.out.println("response = " + result);

25.7. Disabling Data Binding in Doc/Literal

JAX-RPC specification defines Java type mappings for XML types. However, sometimes it is more convenient to send a message by defining a SOAPElement .

This section describes how to use the javax.xml.soap.SOAPElement type regardless of the XML type by using the WSDL-to-Java mapping tool.

25.7.1. Using noDataBinding Option in wsdl2java

The following is an example of a WSDL described as a Document/Literal.

<definitions name="BookQuoteService" ... >
    <types>
        <xsd:schema targetNamespace="...">
            <xsd:complexType name="Book">
                <xsd:sequence>
                    <xsd:element name="title" type="xsd:string" />
                    <xsd:element name="isbn" type="xsd:string" />
                    <xsd:element name="authors" type="xsd:string" />
                </xsd:sequence>
            </xsd:complexType>
            <xsd:element name="Book" type="mh:Book" />
            <xsd:element name="Result" type="xsd:float" />
        </xsd:schema>
    </types>

    <message name="getBookPriceRequest">
        <part name="book" element="mh:Book" />
    </message>
    <message name="getBookPriceResponse">
        <part name="result" element="mh:Result" />
    </message>
    . . .
  
    <binding name="BookServiceSoapBinding" type="mh:BookQuote">
        <soap:binding style="document" ... />
        <operation name="getBookPrice">
            <input>
                <soap:body use="literal" ... />
            </input>
            <output>
                <soap:body use="literal" ... />
            </output>
        </operation>
    </binding>
    . . .
</definitions>

The service endpoint interface generated by wsdl2java from the previous WSDL has the following input parameter.

public interface BookQuote extends java.rmi.Remote {
    public float getBookPrice(sample.nodatabinding.stub.Book book)
    throws java.rmi.RemoteException;
}

When creating an SEI through wsdl2java, if the attribute called 'noDataBinding' is set to 'true' through wsdl2java Ant task or the '–nodatabinding' option is set to 'true' through the command-line tool, the input parameters and return value types must always be javax.xml.soap.SOAPElement, regardless of the XML type.

public interface BookQuote extends java.rmi.Remote {
public javax.xml.soap.SOAPElement
    getBookPrice(javax.xml.soap.SOAPElement book)
    throws java.rmi.RemoteException;
}

The 'nodatabinding' option for wsdl2java is only valid in a Document/Literal WSDL.

In this case, the web service client developer must manually create a message as the SOAPElement type. The following is an example of the web service client that uses the SOAPElement type.

// Creates a FileDataSource from the specified path name
SOAPFactory factroy = SOAPFactory.newInstance();

// Create a SOAPElement object
SOAPElement book = factroy.createElement( "Book", "mh", 
                   "http://www.tmaxsoft.com/j2eews/BookQuote");

SOAPElement title = factroy.createElement("title");
title.addTextNode("Sample for a option: nodatabinding");
book.addChildElement(title);

SOAPElement isbn = factroy.createElement("isbn");
isbn.addTextNode("123-456-789");
book.addChildElement(isbn);

SOAPElement authors = factroy.createElement("authors");
authors.addTextNode("TmaxSoft Co., Ltd.");
book.addChildElement(authors);

// Get a Service port
BookQuote port = new BookQuoteService_Impl().getBookQuotePort();
SOAPElement price = port.getBookPrice(book);
System.out.println("price = " + price.getValue());